diff --git a/README.md b/README.md index e2eb03c..abe705f 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ The following parameters are required for this type of request: The following optional parameters are accepted: -* "count": Number of connections to return (default: 1) +* "count": Number of connections to return (default: 6) * "date": Date on which to find a connection (e.g. 31.12.2017, default: today) * "time": Time at which to find a connection (e.g. 20:00, default: current time) * "mode": May be set to "arr" if "time" should be considered time of arrival @@ -88,17 +88,16 @@ By default, the response will not include some information, including at which stations a change to a different service is required, if applicable. It will, however, return the total number of changes. If additional information is required, it may be requested by adding the "details" parameter to the request. -Note that this request is going to take considerably longer to process. If at -all possible, do not set "count" higher than 1 when requesting details as too -many connections may cause the query to time-out. +Note that this request is going to take about two to three times as long to +process. * [Example "conn" request without details] -(https://bahnapi.xyz/?type=conn&from=Strobl&to=Wien) +(https://bahnapi.xyz/?type=conn&from=Koppl&to=Wien) * [Same request with details] -(https://bahnapi.xyz/?type=conn&from=Strobl&to=Wien&details) +(https://bahnapi.xyz/?type=conn&from=Koppl&to=Wien&details) -If "count" is not set, one connection will be returned. This value may safely be -increased up to 6 if "details" is not set. +If "count" is not set, six connection will be returned, which is the maximum +retrievable at once. * [Example "conn" request for five connections] (https://bahnapi.xyz/?type=conn&from=Thörl&to=Pama&count=5) diff --git a/classes/connection.py b/classes/connection.py index 4844bdb..c7254d5 100644 --- a/classes/connection.py +++ b/classes/connection.py @@ -66,7 +66,7 @@ class Connection: def xml(self, indent = 0, cid = False, frm = True, to = True, deptime = True, arrtime = True, duration = True, changes = True, services = True, via = True, servicekwargs = {}, stationkwargs = {}): out = " " * indent + "\n" % ("" if cid is False else " id=\"%i\"" % cid) - if frm: + if frm and self.depst(): out += " " * indent + " \n" out += self.depst().xml(indent + 2, **stationkwargs) + "\n" out += " " * indent + " \n" @@ -79,32 +79,32 @@ class Connection: out += " " * indent + " \n" - if to: + if to and self.arrst(): out += " " * indent + " \n" out += self.arrst().xml(indent + 2, **stationkwargs) + "\n" out += " " * indent + " \n" - if deptime or arrtime or duration or changes: + if (deptime and self.deptime()) or (arrtime and self.arrtime()) or (duration and self.duration()) or (changes and self.changes()): out += " " * indent + "
\n" - if deptime: + if deptime and self.deptime(): out += " " * indent + " \n" out += " " * indent + " %s\n" % datetime.datetime.strftime(self.deptime(), "%d.%m.%Y") out += " " * indent + " \n" % datetime.datetime.strftime(self.deptime(), "%H:%M") out += " " * indent + " \n" - if arrtime: + if arrtime and self.arrtime(): out += " " * indent + " \n" out += " " * indent + " %s\n" % datetime.datetime.strftime(self.arrtime(), "%d.%m.%Y") out += " " * indent + " \n" % datetime.datetime.strftime(self.arrtime(), "%H:%M") out += " " * indent + " \n" - out += (" " * indent + " %s\n" % self.durationString()) if duration else "" - out += (" " * indent + " %i\n" % self.changes()) if changes else "" + out += (" " * indent + " %s\n" % self.durationString()) if duration and self.duration() else "" + out += (" " * indent + " %i\n" % self.changes()) if changes and self.changes() else "" out += " " * indent + "
\n" - if services: + if services and self.services: out += " " * indent + " \n" for i in range(len(self.services)): @@ -121,7 +121,7 @@ class Connection: out += (" " * indent + " \"@id\": %i,\n" % cid) if cid is not False else "" - if frm: + if frm and self.depst(): out += " " * indent + " \"from\":\n" out += self.depst().json(indent + 2, **stationkwargs) + ",\n" @@ -134,35 +134,35 @@ class Connection: out = "".join(out.rsplit(",", 1)) out += " " * indent + " ],\n" - if to: + if to and self.arrst(): out += " " * indent + " \"to\":\n" out += self.arrst().json(indent + 2, **stationkwargs) + ",\n" - if deptime or arrtime or duration or changes: + if (deptime and self.deptime()) or (arrtime and self.arrtime()) or (duration and self.duration()) or (changes and self.changes()): det = "" det += " " * indent + " \"details\": {\n" - if deptime: + if deptime and self.deptime(): det += " " * indent + " \"departure\": {\n" det += " " * indent + " \"date\": \"%s\",\n" % datetime.datetime.strftime(self.deptime(), "%d.%m.%Y") det += " " * indent + " \"time\": \"%s\"\n" % datetime.datetime.strftime(self.deptime(), "%H:%M") det += " " * indent + " },\n" - if arrtime: + if arrtime and self.arrtime(): det += " " * indent + " \"arrival\": {\n" det += " " * indent + " \"date\": \"%s\",\n" % datetime.datetime.strftime(self.arrtime(), "%d.%m.%Y") det += " " * indent + " \"time\": \"%s\"\n"% datetime.datetime.strftime(self.arrtime(), "%H:%M") det += " " * indent + " },\n" - det += (" " * indent + " \"duration\": \"%s\",\n" % self.durationString()) if duration else "" - det += (" " * indent + " \"changes\": %i,\n" % self.changes()) if changes else "" + det += (" " * indent + " \"duration\": \"%s\",\n" % self.durationString()) if duration and self.duration() else "" + det += (" " * indent + " \"changes\": %i,\n" % self.changes()) if changes and self.changes() else "" det = "".join(det.rsplit(",", 1)) out += det out += " " * indent + " },\n" - if services: + if services and self.services: out += " " * indent + " \"services\": [\n" for i in range(len(self.services)): diff --git a/main.py b/main.py index 297cd20..b68d693 100644 --- a/main.py +++ b/main.py @@ -70,7 +70,7 @@ def application(env, re): yield "\"from\" and \"to\" values are required for this type of request.".encode() return - count = args["count"][0] if "count" in args and args["count"] else 1 + count = args["count"][0] if "count" in args and args["count"] else 6 date = args["date"][0] if "date" in args and args["date"] else datetime.datetime.strftime(datetime.datetime.now(pytz.timezone("Europe/Vienna")),"%d.%m.%Y") time = args["time"][0] if "time" in args and args["time"] else datetime.datetime.strftime(datetime.datetime.now(pytz.timezone("Europe/Vienna")),"%H:%M") mode = True if "mode" in args and args["mode"] and args["mode"][0].lower() == "arr" else False diff --git a/workers/conn.py b/workers/conn.py index 640390e..a56a374 100644 --- a/workers/conn.py +++ b/workers/conn.py @@ -2,6 +2,8 @@ from bs4 import BeautifulSoup import requests import datetime import pytz +import threading +import queue import workers.val from classes import * @@ -9,9 +11,89 @@ from classes import * def getStation(name): return list(workers.val.validateName(name))[0] +def getService(sid, lines, q): + dep = lines[0] + arr = lines[1] + det = lines[2] + + depst = list(workers.val.validateName(dep.find("td", { "class": "station" }).findAll("a")[0].string))[0] + depdate = dep.find("td", { "class": "date" }).string.strip() or None + deptime = dep.find("td", { "class": "timeValue" }).find("span").string.split()[1].strip() + + depprog = (dep.find("span", { "class": "prognosis" }).find("span") or dep.find("span", { "class": "prognosis" })).string.strip() or None + depplat = (dep.find("td", { "class": "platform" }).find("span") or dep.find("td", { "class": "platform" })).string.strip() or None + + walk = dep.find("img", { "class": "product" }).get("src") == "/img/vs_oebb/fuss_pic.gif" + name = dep.find("img", { "class": "product" }).get("alt") if not walk else "Walk" + + arrst = list(workers.val.validateName(arr.find("td", { "class": "station" }).findAll("a")[0].string))[0] + arrdate = (arr.find("td", { "class": "date" }).find("span") or arr.find("td", { "class": "date" })).string.strip() or depdate + arrtime = arr.find("td", { "class": "timeValue" }).find("span").string.split()[1].strip() + arrprog = (arr.find("span", { "class": "prognosis" }).find("span") or arr.find("span", { "class": "prognosis" })).string.strip() or None + arrplat = (arr.find("td", { "class": "platform" }).find("span") or arr.find("td", { "class": "platform" })).string.strip() or None + + if arrdate and not depdate: + arrdts = datetime.datetime.strptime(arrdate, "%d.%m.%Y") + depdts = arrdts - datetime.timedelta(days=1) + depdate = datetime.datetime.strftime(depdts, "%d.%m.%Y") + + if not (walk and depdate): + purl = dep.find("td", { "class": "product" }).find("a").get("href") + psource = requests.get(purl).text + + zuppa = BeautifulSoup(psource, "html5lib") + depdate = zuppa.findAll("div", { "class": "block" })[1].text.strip() + arrdate = arrdate or depdate + dest = None if walk else list(workers.val.validateName(zuppa.findAll("div", { "class": "block" })[2].text.split(":")[1].strip()))[0] + + depts = datetime.datetime.strptime("%s %s" % (depdate, deptime), "%d.%m.%Y %H:%M") + arrts = datetime.datetime.strptime("%s %s" % (arrdate, arrtime), "%d.%m.%Y %H:%M") + + depprog = deptime if depprog == "pünktlich" else depprog + arrprog = arrtime if arrprog == "pünktlich" else arrprog + + svc = Service(name, depst, depts, arrst, arrts, dest, depplat, depprog, arrplat, arrprog) + q.put((sid, svc)) + +def getDetails(cid, url, q, via = []): + ssource = requests.get(url).text + suppe = BeautifulSoup(ssource, "html5lib") + + cont = suppe.find("tr", id="trC0-%i" % cid) + if not cont: + return + +# buyurl = None +# +# for url in cont.findAll("a"): +# if url.get("href") and "https://tickets.oebb.at/de/ticket/ticket?" in url.get("href"): +# buyurl = url.get("href") + + conn = Connection(True) + + for vst in via: + conn.addVia(vst) + + lines = cont.findAll("tr", { "class": "tpDetails" })[1:] + threads = [] + iq = queue.PriorityQueue() + + for line in range(0, len(lines), 3): + t = threading.Thread(target=getService, args=(line, lines[line:line + 3], iq)) + t.start() + threads += [t] + + for t in threads: + t.join() + + while not iq.empty(): + conn.addService(iq.get()[1]) + + q.put((cid, conn)) + def connRequest(frm, to, count = 3, time = datetime.datetime.now(), mode = False, details = False, via = []): - outdate = datetime.datetime.strftime(time,"%d.%m.%Y") - outtime = datetime.datetime.strftime(time,"%H:%M") + outdate = datetime.datetime.strftime(time, "%d.%m.%Y") + outtime = datetime.datetime.strftime(time, "%H:%M") url = "http://fahrplan.oebb.at/bin/query.exe/dn?start=1&S=%s&Z=%s&REQ0JourneyDate=%s&time=%s&REQ0HafasNumCons0=%s%s" % (frm.extid if frm.extid else frm.name, to.extid if to.extid else to.name, outdate, outtime, count, "×el=arrive" if mode else "") @@ -26,72 +108,25 @@ def connRequest(frm, to, count = 3, time = datetime.datetime.now(), mode = False juha = BeautifulSoup(source, "html5lib") if details: + conns = [] + for a in juha.findAll("a"): if a.get("href") and "HWAI=CONNECTION$" in a.get("href"): - dpage = a.get("href") + conns += [a.get("href")] - ssource = requests.get(dpage).text - suppe = BeautifulSoup(ssource, "html5lib") - - for i in range(0, count): - cont = suppe.find("tr", id="trC0-%i" % i) - if not cont: - break + threads = [] + q = queue.PriorityQueue() -# buyurl = None -# -# for url in cont.findAll("a"): -# if url.get("href") and "https://tickets.oebb.at/de/ticket/ticket?" in url.get("href"): -# buyurl = url.get("href") + for i in range(len(conns[:-1])): + t = threading.Thread(target=getDetails, args=(i, conns[i], q, via)) + t.start() + threads += [t] - conn = Connection(details) + for t in threads: + t.join() - for vst in via: - conn.addVia(vst) - - lines = cont.findAll("tr", { "class": "tpDetails" })[1:] - cdate = None - - for line in range(0, len(lines), 3): - serv = lines[line:line + 3] - dep = serv[0] - arr = serv[1] - det = serv[2] - - depst = list(workers.val.validateName(dep.find("td", { "class": "station" }).findAll("a")[0].string))[0] - depdate = dep.find("td", { "class": "date" }).string.strip() or cdate - deptime = dep.find("td", { "class": "timeValue" }).find("span").string.split()[1].strip() - depprog = (dep.find("span", { "class": "prognosis" }).find("span") or dep.find("span", { "class": "prognosis" })).string.strip() or None - depplat = (dep.find("td", { "class": "platform" }).find("span") or dep.find("td", { "class": "platform" })).string.strip() or None - - walk = dep.find("img", { "class": "product" }).get("src") == "/img/vs_oebb/fuss_pic.gif" - name = dep.find("img", { "class": "product" }).get("alt") if not walk else "Walk" - - if not walk: - purl = dep.find("td", { "class": "product" }).find("a").get("href") - psource = requests.get(purl).text - - zuppa = BeautifulSoup(psource, "html5lib") - dest = list(workers.val.validateName(zuppa.findAll("div", { "class": "block" })[2].text.split(":")[1].strip()))[0] - - arrst = list(workers.val.validateName(arr.find("td", { "class": "station" }).findAll("a")[0].string))[0] - arrdate = (arr.find("td", { "class": "date" }).find("span") or arr.find("td", { "class": "date" })).string.strip() or depdate - arrtime = arr.find("td", { "class": "timeValue" }).find("span").string.split()[1].strip() - arrprog = (arr.find("span", { "class": "prognosis" }).find("span") or arr.find("span", { "class": "prognosis" })).string.strip() or None - arrplat = (arr.find("td", { "class": "platform" }).find("span") or arr.find("td", { "class": "platform" })).string.strip() or None - - depts = datetime.datetime.strptime("%s %s" % (depdate, deptime), "%d.%m.%Y %H:%M") - arrts = datetime.datetime.strptime("%s %s" % (arrdate, arrtime), "%d.%m.%Y %H:%M") - - depprog = deptime if depprog == "pünktlich" else depprog - arrprog = arrtime if arrprog == "pünktlich" else arrprog - - cdate = arrdate - - svc = Service(name, depst, depts, arrst, arrts, dest, depplat, depprog, arrplat, arrprog) - conn.addService(svc) - - yield conn + while not q.empty(): + yield q.get()[1] else: for i in range(0, count):