Backend changes and sql convenience
- Added sql files to create and drop db and to feed example data. - Added bridge to import (and later export) data from a gtfs.GTFS object to the database. - Updated models and migrations to implement the whole GTFS reference.
This commit is contained in:
parent
e8d02517af
commit
af5a2c862d
8
bin/calc-prorate
Executable file
8
bin/calc-prorate
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from tempora import calculate_prorated_values
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(calculate_prorated_values())
|
||||||
8
bin/cheroot
Executable file
8
bin/cheroot
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from cheroot.cli import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(main())
|
||||||
8
bin/cherryd
Executable file
8
bin/cherryd
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from cherrypy.__main__ import run
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(run())
|
||||||
473
bin/dumppdf.py
Executable file
473
bin/dumppdf.py
Executable file
|
|
@ -0,0 +1,473 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
"""Extract pdf structure in XML format"""
|
||||||
|
import logging
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from typing import Any, Container, Dict, Iterable, List, Optional, TextIO, Union, cast
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
import pdfminer
|
||||||
|
from pdfminer.pdfdocument import PDFDocument, PDFNoOutlines, PDFXRefFallback
|
||||||
|
from pdfminer.pdfpage import PDFPage
|
||||||
|
from pdfminer.pdfparser import PDFParser
|
||||||
|
from pdfminer.pdftypes import PDFObjectNotFound, PDFValueError
|
||||||
|
from pdfminer.pdftypes import PDFStream, PDFObjRef, resolve1, stream_value
|
||||||
|
from pdfminer.psparser import PSKeyword, PSLiteral, LIT
|
||||||
|
from pdfminer.utils import isnumber
|
||||||
|
|
||||||
|
logging.basicConfig()
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ESC_PAT = re.compile(r'[\000-\037&<>()"\042\047\134\177-\377]')
|
||||||
|
|
||||||
|
|
||||||
|
def escape(s: Union[str, bytes]) -> str:
|
||||||
|
if isinstance(s, bytes):
|
||||||
|
us = str(s, "latin-1")
|
||||||
|
else:
|
||||||
|
us = s
|
||||||
|
return ESC_PAT.sub(lambda m: "&#%d;" % ord(m.group(0)), us)
|
||||||
|
|
||||||
|
|
||||||
|
def dumpxml(out: TextIO, obj: object, codec: Optional[str] = None) -> None:
|
||||||
|
if obj is None:
|
||||||
|
out.write("<null />")
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
out.write('<dict size="%d">\n' % len(obj))
|
||||||
|
for (k, v) in obj.items():
|
||||||
|
out.write("<key>%s</key>\n" % k)
|
||||||
|
out.write("<value>")
|
||||||
|
dumpxml(out, v)
|
||||||
|
out.write("</value>\n")
|
||||||
|
out.write("</dict>")
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(obj, list):
|
||||||
|
out.write('<list size="%d">\n' % len(obj))
|
||||||
|
for v in obj:
|
||||||
|
dumpxml(out, v)
|
||||||
|
out.write("\n")
|
||||||
|
out.write("</list>")
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(obj, (str, bytes)):
|
||||||
|
out.write('<string size="%d">%s</string>' % (len(obj), escape(obj)))
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(obj, PDFStream):
|
||||||
|
if codec == "raw":
|
||||||
|
# Bug: writing bytes to text I/O. This will raise TypeError.
|
||||||
|
out.write(obj.get_rawdata()) # type: ignore [arg-type]
|
||||||
|
elif codec == "binary":
|
||||||
|
# Bug: writing bytes to text I/O. This will raise TypeError.
|
||||||
|
out.write(obj.get_data()) # type: ignore [arg-type]
|
||||||
|
else:
|
||||||
|
out.write("<stream>\n<props>\n")
|
||||||
|
dumpxml(out, obj.attrs)
|
||||||
|
out.write("\n</props>\n")
|
||||||
|
if codec == "text":
|
||||||
|
data = obj.get_data()
|
||||||
|
out.write('<data size="%d">%s</data>\n' % (len(data), escape(data)))
|
||||||
|
out.write("</stream>")
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(obj, PDFObjRef):
|
||||||
|
out.write('<ref id="%d" />' % obj.objid)
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(obj, PSKeyword):
|
||||||
|
# Likely bug: obj.name is bytes, not str
|
||||||
|
out.write("<keyword>%s</keyword>" % obj.name) # type: ignore [str-bytes-safe]
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(obj, PSLiteral):
|
||||||
|
# Likely bug: obj.name may be bytes, not str
|
||||||
|
out.write("<literal>%s</literal>" % obj.name) # type: ignore [str-bytes-safe]
|
||||||
|
return
|
||||||
|
|
||||||
|
if isnumber(obj):
|
||||||
|
out.write("<number>%s</number>" % obj)
|
||||||
|
return
|
||||||
|
|
||||||
|
raise TypeError(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def dumptrailers(
|
||||||
|
out: TextIO, doc: PDFDocument, show_fallback_xref: bool = False
|
||||||
|
) -> None:
|
||||||
|
for xref in doc.xrefs:
|
||||||
|
if not isinstance(xref, PDFXRefFallback) or show_fallback_xref:
|
||||||
|
out.write("<trailer>\n")
|
||||||
|
dumpxml(out, xref.get_trailer())
|
||||||
|
out.write("\n</trailer>\n\n")
|
||||||
|
no_xrefs = all(isinstance(xref, PDFXRefFallback) for xref in doc.xrefs)
|
||||||
|
if no_xrefs and not show_fallback_xref:
|
||||||
|
msg = (
|
||||||
|
"This PDF does not have an xref. Use --show-fallback-xref if "
|
||||||
|
"you want to display the content of a fallback xref that "
|
||||||
|
"contains all objects."
|
||||||
|
)
|
||||||
|
logger.warning(msg)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def dumpallobjs(
|
||||||
|
out: TextIO,
|
||||||
|
doc: PDFDocument,
|
||||||
|
codec: Optional[str] = None,
|
||||||
|
show_fallback_xref: bool = False,
|
||||||
|
) -> None:
|
||||||
|
visited = set()
|
||||||
|
out.write("<pdf>")
|
||||||
|
for xref in doc.xrefs:
|
||||||
|
for objid in xref.get_objids():
|
||||||
|
if objid in visited:
|
||||||
|
continue
|
||||||
|
visited.add(objid)
|
||||||
|
try:
|
||||||
|
obj = doc.getobj(objid)
|
||||||
|
if obj is None:
|
||||||
|
continue
|
||||||
|
out.write('<object id="%d">\n' % objid)
|
||||||
|
dumpxml(out, obj, codec=codec)
|
||||||
|
out.write("\n</object>\n\n")
|
||||||
|
except PDFObjectNotFound as e:
|
||||||
|
print("not found: %r" % e)
|
||||||
|
dumptrailers(out, doc, show_fallback_xref)
|
||||||
|
out.write("</pdf>")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def dumpoutline(
|
||||||
|
outfp: TextIO,
|
||||||
|
fname: str,
|
||||||
|
objids: Any,
|
||||||
|
pagenos: Container[int],
|
||||||
|
password: str = "",
|
||||||
|
dumpall: bool = False,
|
||||||
|
codec: Optional[str] = None,
|
||||||
|
extractdir: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
|
fp = open(fname, "rb")
|
||||||
|
parser = PDFParser(fp)
|
||||||
|
doc = PDFDocument(parser, password)
|
||||||
|
pages = {
|
||||||
|
page.pageid: pageno
|
||||||
|
for (pageno, page) in enumerate(PDFPage.create_pages(doc), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
def resolve_dest(dest: object) -> Any:
|
||||||
|
if isinstance(dest, (str, bytes)):
|
||||||
|
dest = resolve1(doc.get_dest(dest))
|
||||||
|
elif isinstance(dest, PSLiteral):
|
||||||
|
dest = resolve1(doc.get_dest(dest.name))
|
||||||
|
if isinstance(dest, dict):
|
||||||
|
dest = dest["D"]
|
||||||
|
if isinstance(dest, PDFObjRef):
|
||||||
|
dest = dest.resolve()
|
||||||
|
return dest
|
||||||
|
|
||||||
|
try:
|
||||||
|
outlines = doc.get_outlines()
|
||||||
|
outfp.write("<outlines>\n")
|
||||||
|
for (level, title, dest, a, se) in outlines:
|
||||||
|
pageno = None
|
||||||
|
if dest:
|
||||||
|
dest = resolve_dest(dest)
|
||||||
|
pageno = pages[dest[0].objid]
|
||||||
|
elif a:
|
||||||
|
action = a
|
||||||
|
if isinstance(action, dict):
|
||||||
|
subtype = action.get("S")
|
||||||
|
if subtype and repr(subtype) == "/'GoTo'" and action.get("D"):
|
||||||
|
dest = resolve_dest(action["D"])
|
||||||
|
pageno = pages[dest[0].objid]
|
||||||
|
s = escape(title)
|
||||||
|
outfp.write('<outline level="{!r}" title="{}">\n'.format(level, s))
|
||||||
|
if dest is not None:
|
||||||
|
outfp.write("<dest>")
|
||||||
|
dumpxml(outfp, dest)
|
||||||
|
outfp.write("</dest>\n")
|
||||||
|
if pageno is not None:
|
||||||
|
outfp.write("<pageno>%r</pageno>\n" % pageno)
|
||||||
|
outfp.write("</outline>\n")
|
||||||
|
outfp.write("</outlines>\n")
|
||||||
|
except PDFNoOutlines:
|
||||||
|
pass
|
||||||
|
parser.close()
|
||||||
|
fp.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
LITERAL_FILESPEC = LIT("Filespec")
|
||||||
|
LITERAL_EMBEDDEDFILE = LIT("EmbeddedFile")
|
||||||
|
|
||||||
|
|
||||||
|
def extractembedded(fname: str, password: str, extractdir: str) -> None:
|
||||||
|
def extract1(objid: int, obj: Dict[str, Any]) -> None:
|
||||||
|
filename = os.path.basename(obj.get("UF") or cast(bytes, obj.get("F")).decode())
|
||||||
|
fileref = obj["EF"].get("UF") or obj["EF"].get("F")
|
||||||
|
fileobj = doc.getobj(fileref.objid)
|
||||||
|
if not isinstance(fileobj, PDFStream):
|
||||||
|
error_msg = (
|
||||||
|
"unable to process PDF: reference for %r is not a "
|
||||||
|
"PDFStream" % filename
|
||||||
|
)
|
||||||
|
raise PDFValueError(error_msg)
|
||||||
|
if fileobj.get("Type") is not LITERAL_EMBEDDEDFILE:
|
||||||
|
raise PDFValueError(
|
||||||
|
"unable to process PDF: reference for %r "
|
||||||
|
"is not an EmbeddedFile" % (filename)
|
||||||
|
)
|
||||||
|
path = os.path.join(extractdir, "%.6d-%s" % (objid, filename))
|
||||||
|
if os.path.exists(path):
|
||||||
|
raise IOError("file exists: %r" % path)
|
||||||
|
print("extracting: %r" % path)
|
||||||
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||||
|
out = open(path, "wb")
|
||||||
|
out.write(fileobj.get_data())
|
||||||
|
out.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(fname, "rb") as fp:
|
||||||
|
parser = PDFParser(fp)
|
||||||
|
doc = PDFDocument(parser, password)
|
||||||
|
extracted_objids = set()
|
||||||
|
for xref in doc.xrefs:
|
||||||
|
for objid in xref.get_objids():
|
||||||
|
obj = doc.getobj(objid)
|
||||||
|
if (
|
||||||
|
objid not in extracted_objids
|
||||||
|
and isinstance(obj, dict)
|
||||||
|
and obj.get("Type") is LITERAL_FILESPEC
|
||||||
|
):
|
||||||
|
extracted_objids.add(objid)
|
||||||
|
extract1(objid, obj)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def dumppdf(
|
||||||
|
outfp: TextIO,
|
||||||
|
fname: str,
|
||||||
|
objids: Iterable[int],
|
||||||
|
pagenos: Container[int],
|
||||||
|
password: str = "",
|
||||||
|
dumpall: bool = False,
|
||||||
|
codec: Optional[str] = None,
|
||||||
|
extractdir: Optional[str] = None,
|
||||||
|
show_fallback_xref: bool = False,
|
||||||
|
) -> None:
|
||||||
|
fp = open(fname, "rb")
|
||||||
|
parser = PDFParser(fp)
|
||||||
|
doc = PDFDocument(parser, password)
|
||||||
|
if objids:
|
||||||
|
for objid in objids:
|
||||||
|
obj = doc.getobj(objid)
|
||||||
|
dumpxml(outfp, obj, codec=codec)
|
||||||
|
if pagenos:
|
||||||
|
for (pageno, page) in enumerate(PDFPage.create_pages(doc)):
|
||||||
|
if pageno in pagenos:
|
||||||
|
if codec:
|
||||||
|
for obj in page.contents:
|
||||||
|
obj = stream_value(obj)
|
||||||
|
dumpxml(outfp, obj, codec=codec)
|
||||||
|
else:
|
||||||
|
dumpxml(outfp, page.attrs)
|
||||||
|
if dumpall:
|
||||||
|
dumpallobjs(outfp, doc, codec, show_fallback_xref)
|
||||||
|
if (not objids) and (not pagenos) and (not dumpall):
|
||||||
|
dumptrailers(outfp, doc, show_fallback_xref)
|
||||||
|
fp.close()
|
||||||
|
if codec not in ("raw", "binary"):
|
||||||
|
outfp.write("\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def create_parser() -> ArgumentParser:
|
||||||
|
parser = ArgumentParser(description=__doc__, add_help=True)
|
||||||
|
parser.add_argument(
|
||||||
|
"files",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
nargs="+",
|
||||||
|
help="One or more paths to PDF files.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--version",
|
||||||
|
"-v",
|
||||||
|
action="version",
|
||||||
|
version="pdfminer.six v{}".format(pdfminer.__version__),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--debug",
|
||||||
|
"-d",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Use debug logging level.",
|
||||||
|
)
|
||||||
|
procedure_parser = parser.add_mutually_exclusive_group()
|
||||||
|
procedure_parser.add_argument(
|
||||||
|
"--extract-toc",
|
||||||
|
"-T",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Extract structure of outline",
|
||||||
|
)
|
||||||
|
procedure_parser.add_argument(
|
||||||
|
"--extract-embedded", "-E", type=str, help="Extract embedded files"
|
||||||
|
)
|
||||||
|
|
||||||
|
parse_params = parser.add_argument_group(
|
||||||
|
"Parser", description="Used during PDF parsing"
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--page-numbers",
|
||||||
|
type=int,
|
||||||
|
default=None,
|
||||||
|
nargs="+",
|
||||||
|
help="A space-seperated list of page numbers to parse.",
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--pagenos",
|
||||||
|
"-p",
|
||||||
|
type=str,
|
||||||
|
help="A comma-separated list of page numbers to parse. Included for "
|
||||||
|
"legacy applications, use --page-numbers for more idiomatic "
|
||||||
|
"argument entry.",
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--objects",
|
||||||
|
"-i",
|
||||||
|
type=str,
|
||||||
|
help="Comma separated list of object numbers to extract",
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--all",
|
||||||
|
"-a",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="If the structure of all objects should be extracted",
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--show-fallback-xref",
|
||||||
|
action="store_true",
|
||||||
|
help="Additionally show the fallback xref. Use this if the PDF "
|
||||||
|
"has zero or only invalid xref's. This setting is ignored if "
|
||||||
|
"--extract-toc or --extract-embedded is used.",
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--password",
|
||||||
|
"-P",
|
||||||
|
type=str,
|
||||||
|
default="",
|
||||||
|
help="The password to use for decrypting PDF file.",
|
||||||
|
)
|
||||||
|
|
||||||
|
output_params = parser.add_argument_group(
|
||||||
|
"Output", description="Used during output generation."
|
||||||
|
)
|
||||||
|
output_params.add_argument(
|
||||||
|
"--outfile",
|
||||||
|
"-o",
|
||||||
|
type=str,
|
||||||
|
default="-",
|
||||||
|
help='Path to file where output is written. Or "-" (default) to '
|
||||||
|
"write to stdout.",
|
||||||
|
)
|
||||||
|
codec_parser = output_params.add_mutually_exclusive_group()
|
||||||
|
codec_parser.add_argument(
|
||||||
|
"--raw-stream",
|
||||||
|
"-r",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Write stream objects without encoding",
|
||||||
|
)
|
||||||
|
codec_parser.add_argument(
|
||||||
|
"--binary-stream",
|
||||||
|
"-b",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Write stream objects with binary encoding",
|
||||||
|
)
|
||||||
|
codec_parser.add_argument(
|
||||||
|
"--text-stream",
|
||||||
|
"-t",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Write stream objects as plain text",
|
||||||
|
)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: Optional[List[str]] = None) -> None:
|
||||||
|
parser = create_parser()
|
||||||
|
args = parser.parse_args(args=argv)
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
if args.outfile == "-":
|
||||||
|
outfp = sys.stdout
|
||||||
|
else:
|
||||||
|
outfp = open(args.outfile, "w")
|
||||||
|
|
||||||
|
if args.objects:
|
||||||
|
objids = [int(x) for x in args.objects.split(",")]
|
||||||
|
else:
|
||||||
|
objids = []
|
||||||
|
|
||||||
|
if args.page_numbers:
|
||||||
|
pagenos = {x - 1 for x in args.page_numbers}
|
||||||
|
elif args.pagenos:
|
||||||
|
pagenos = {int(x) - 1 for x in args.pagenos.split(",")}
|
||||||
|
else:
|
||||||
|
pagenos = set()
|
||||||
|
|
||||||
|
password = args.password
|
||||||
|
|
||||||
|
if args.raw_stream:
|
||||||
|
codec: Optional[str] = "raw"
|
||||||
|
elif args.binary_stream:
|
||||||
|
codec = "binary"
|
||||||
|
elif args.text_stream:
|
||||||
|
codec = "text"
|
||||||
|
else:
|
||||||
|
codec = None
|
||||||
|
|
||||||
|
for fname in args.files:
|
||||||
|
if args.extract_toc:
|
||||||
|
dumpoutline(
|
||||||
|
outfp,
|
||||||
|
fname,
|
||||||
|
objids,
|
||||||
|
pagenos,
|
||||||
|
password=password,
|
||||||
|
dumpall=args.all,
|
||||||
|
codec=codec,
|
||||||
|
extractdir=None,
|
||||||
|
)
|
||||||
|
elif args.extract_embedded:
|
||||||
|
extractembedded(fname, password=password, extractdir=args.extract_embedded)
|
||||||
|
else:
|
||||||
|
dumppdf(
|
||||||
|
outfp,
|
||||||
|
fname,
|
||||||
|
objids,
|
||||||
|
pagenos,
|
||||||
|
password=password,
|
||||||
|
dumpall=args.all,
|
||||||
|
codec=codec,
|
||||||
|
extractdir=None,
|
||||||
|
show_fallback_xref=args.show_fallback_xref,
|
||||||
|
)
|
||||||
|
|
||||||
|
outfp.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
8
bin/futurize
Executable file
8
bin/futurize
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from libfuturize.main import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(main())
|
||||||
8
bin/nltk
Executable file
8
bin/nltk
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from nltk.cli import cli
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(cli())
|
||||||
8
bin/normalizer
Executable file
8
bin/normalizer
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from charset_normalizer.cli import cli_detect
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(cli_detect())
|
||||||
8
bin/pasteurize
Executable file
8
bin/pasteurize
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from libpasteurize.main import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(main())
|
||||||
317
bin/pdf2txt.py
Executable file
317
bin/pdf2txt.py
Executable file
|
|
@ -0,0 +1,317 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
"""A command line tool for extracting text and images from PDF and
|
||||||
|
output it to plain text, html, xml or tags."""
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from typing import Any, Container, Iterable, List, Optional
|
||||||
|
|
||||||
|
import pdfminer.high_level
|
||||||
|
from pdfminer.layout import LAParams
|
||||||
|
from pdfminer.utils import AnyIO
|
||||||
|
|
||||||
|
logging.basicConfig()
|
||||||
|
|
||||||
|
OUTPUT_TYPES = ((".htm", "html"), (".html", "html"), (".xml", "xml"), (".tag", "tag"))
|
||||||
|
|
||||||
|
|
||||||
|
def float_or_disabled(x: str) -> Optional[float]:
|
||||||
|
if x.lower().strip() == "disabled":
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return float(x)
|
||||||
|
except ValueError:
|
||||||
|
raise argparse.ArgumentTypeError("invalid float value: {}".format(x))
|
||||||
|
|
||||||
|
|
||||||
|
def extract_text(
|
||||||
|
files: Iterable[str] = [],
|
||||||
|
outfile: str = "-",
|
||||||
|
laparams: Optional[LAParams] = None,
|
||||||
|
output_type: str = "text",
|
||||||
|
codec: str = "utf-8",
|
||||||
|
strip_control: bool = False,
|
||||||
|
maxpages: int = 0,
|
||||||
|
page_numbers: Optional[Container[int]] = None,
|
||||||
|
password: str = "",
|
||||||
|
scale: float = 1.0,
|
||||||
|
rotation: int = 0,
|
||||||
|
layoutmode: str = "normal",
|
||||||
|
output_dir: Optional[str] = None,
|
||||||
|
debug: bool = False,
|
||||||
|
disable_caching: bool = False,
|
||||||
|
**kwargs: Any
|
||||||
|
) -> AnyIO:
|
||||||
|
if not files:
|
||||||
|
raise ValueError("Must provide files to work upon!")
|
||||||
|
|
||||||
|
if output_type == "text" and outfile != "-":
|
||||||
|
for override, alttype in OUTPUT_TYPES:
|
||||||
|
if outfile.endswith(override):
|
||||||
|
output_type = alttype
|
||||||
|
|
||||||
|
if outfile == "-":
|
||||||
|
outfp: AnyIO = sys.stdout
|
||||||
|
if sys.stdout.encoding is not None:
|
||||||
|
codec = "utf-8"
|
||||||
|
else:
|
||||||
|
outfp = open(outfile, "wb")
|
||||||
|
|
||||||
|
for fname in files:
|
||||||
|
with open(fname, "rb") as fp:
|
||||||
|
pdfminer.high_level.extract_text_to_fp(fp, **locals())
|
||||||
|
return outfp
|
||||||
|
|
||||||
|
|
||||||
|
def create_parser() -> argparse.ArgumentParser:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__, add_help=True)
|
||||||
|
parser.add_argument(
|
||||||
|
"files",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
nargs="+",
|
||||||
|
help="One or more paths to PDF files.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--version",
|
||||||
|
"-v",
|
||||||
|
action="version",
|
||||||
|
version="pdfminer.six v{}".format(pdfminer.__version__),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--debug",
|
||||||
|
"-d",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Use debug logging level.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--disable-caching",
|
||||||
|
"-C",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="If caching or resources, such as fonts, should be disabled.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parse_params = parser.add_argument_group(
|
||||||
|
"Parser", description="Used during PDF parsing"
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--page-numbers",
|
||||||
|
type=int,
|
||||||
|
default=None,
|
||||||
|
nargs="+",
|
||||||
|
help="A space-seperated list of page numbers to parse.",
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--pagenos",
|
||||||
|
"-p",
|
||||||
|
type=str,
|
||||||
|
help="A comma-separated list of page numbers to parse. "
|
||||||
|
"Included for legacy applications, use --page-numbers "
|
||||||
|
"for more idiomatic argument entry.",
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--maxpages",
|
||||||
|
"-m",
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help="The maximum number of pages to parse.",
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--password",
|
||||||
|
"-P",
|
||||||
|
type=str,
|
||||||
|
default="",
|
||||||
|
help="The password to use for decrypting PDF file.",
|
||||||
|
)
|
||||||
|
parse_params.add_argument(
|
||||||
|
"--rotation",
|
||||||
|
"-R",
|
||||||
|
default=0,
|
||||||
|
type=int,
|
||||||
|
help="The number of degrees to rotate the PDF "
|
||||||
|
"before other types of processing.",
|
||||||
|
)
|
||||||
|
|
||||||
|
la_params = LAParams() # will be used for defaults
|
||||||
|
la_param_group = parser.add_argument_group(
|
||||||
|
"Layout analysis", description="Used during layout analysis."
|
||||||
|
)
|
||||||
|
la_param_group.add_argument(
|
||||||
|
"--no-laparams",
|
||||||
|
"-n",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="If layout analysis parameters should be ignored.",
|
||||||
|
)
|
||||||
|
la_param_group.add_argument(
|
||||||
|
"--detect-vertical",
|
||||||
|
"-V",
|
||||||
|
default=la_params.detect_vertical,
|
||||||
|
action="store_true",
|
||||||
|
help="If vertical text should be considered during layout analysis",
|
||||||
|
)
|
||||||
|
la_param_group.add_argument(
|
||||||
|
"--line-overlap",
|
||||||
|
type=float,
|
||||||
|
default=la_params.line_overlap,
|
||||||
|
help="If two characters have more overlap than this they "
|
||||||
|
"are considered to be on the same line. The overlap is specified "
|
||||||
|
"relative to the minimum height of both characters.",
|
||||||
|
)
|
||||||
|
la_param_group.add_argument(
|
||||||
|
"--char-margin",
|
||||||
|
"-M",
|
||||||
|
type=float,
|
||||||
|
default=la_params.char_margin,
|
||||||
|
help="If two characters are closer together than this margin they "
|
||||||
|
"are considered to be part of the same line. The margin is "
|
||||||
|
"specified relative to the width of the character.",
|
||||||
|
)
|
||||||
|
la_param_group.add_argument(
|
||||||
|
"--word-margin",
|
||||||
|
"-W",
|
||||||
|
type=float,
|
||||||
|
default=la_params.word_margin,
|
||||||
|
help="If two characters on the same line are further apart than this "
|
||||||
|
"margin then they are considered to be two separate words, and "
|
||||||
|
"an intermediate space will be added for readability. The margin "
|
||||||
|
"is specified relative to the width of the character.",
|
||||||
|
)
|
||||||
|
la_param_group.add_argument(
|
||||||
|
"--line-margin",
|
||||||
|
"-L",
|
||||||
|
type=float,
|
||||||
|
default=la_params.line_margin,
|
||||||
|
help="If two lines are close together they are considered to "
|
||||||
|
"be part of the same paragraph. The margin is specified "
|
||||||
|
"relative to the height of a line.",
|
||||||
|
)
|
||||||
|
la_param_group.add_argument(
|
||||||
|
"--boxes-flow",
|
||||||
|
"-F",
|
||||||
|
type=float_or_disabled,
|
||||||
|
default=la_params.boxes_flow,
|
||||||
|
help="Specifies how much a horizontal and vertical position of a "
|
||||||
|
"text matters when determining the order of lines. The value "
|
||||||
|
"should be within the range of -1.0 (only horizontal position "
|
||||||
|
"matters) to +1.0 (only vertical position matters). You can also "
|
||||||
|
"pass `disabled` to disable advanced layout analysis, and "
|
||||||
|
"instead return text based on the position of the bottom left "
|
||||||
|
"corner of the text box.",
|
||||||
|
)
|
||||||
|
la_param_group.add_argument(
|
||||||
|
"--all-texts",
|
||||||
|
"-A",
|
||||||
|
default=la_params.all_texts,
|
||||||
|
action="store_true",
|
||||||
|
help="If layout analysis should be performed on text in figures.",
|
||||||
|
)
|
||||||
|
|
||||||
|
output_params = parser.add_argument_group(
|
||||||
|
"Output", description="Used during output generation."
|
||||||
|
)
|
||||||
|
output_params.add_argument(
|
||||||
|
"--outfile",
|
||||||
|
"-o",
|
||||||
|
type=str,
|
||||||
|
default="-",
|
||||||
|
help="Path to file where output is written. "
|
||||||
|
'Or "-" (default) to write to stdout.',
|
||||||
|
)
|
||||||
|
output_params.add_argument(
|
||||||
|
"--output_type",
|
||||||
|
"-t",
|
||||||
|
type=str,
|
||||||
|
default="text",
|
||||||
|
help="Type of output to generate {text,html,xml,tag}.",
|
||||||
|
)
|
||||||
|
output_params.add_argument(
|
||||||
|
"--codec",
|
||||||
|
"-c",
|
||||||
|
type=str,
|
||||||
|
default="utf-8",
|
||||||
|
help="Text encoding to use in output file.",
|
||||||
|
)
|
||||||
|
output_params.add_argument(
|
||||||
|
"--output-dir",
|
||||||
|
"-O",
|
||||||
|
default=None,
|
||||||
|
help="The output directory to put extracted images in. If not given, "
|
||||||
|
"images are not extracted.",
|
||||||
|
)
|
||||||
|
output_params.add_argument(
|
||||||
|
"--layoutmode",
|
||||||
|
"-Y",
|
||||||
|
default="normal",
|
||||||
|
type=str,
|
||||||
|
help="Type of layout to use when generating html "
|
||||||
|
"{normal,exact,loose}. If normal,each line is"
|
||||||
|
" positioned separately in the html. If exact"
|
||||||
|
", each character is positioned separately in"
|
||||||
|
" the html. If loose, same result as normal "
|
||||||
|
"but with an additional newline after each "
|
||||||
|
"text line. Only used when output_type is html.",
|
||||||
|
)
|
||||||
|
output_params.add_argument(
|
||||||
|
"--scale",
|
||||||
|
"-s",
|
||||||
|
type=float,
|
||||||
|
default=1.0,
|
||||||
|
help="The amount of zoom to use when generating html file. "
|
||||||
|
"Only used when output_type is html.",
|
||||||
|
)
|
||||||
|
output_params.add_argument(
|
||||||
|
"--strip-control",
|
||||||
|
"-S",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Remove control statement from text. "
|
||||||
|
"Only used when output_type is xml.",
|
||||||
|
)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(args: Optional[List[str]]) -> argparse.Namespace:
|
||||||
|
parsed_args = create_parser().parse_args(args=args)
|
||||||
|
|
||||||
|
# Propagate parsed layout parameters to LAParams object
|
||||||
|
if parsed_args.no_laparams:
|
||||||
|
parsed_args.laparams = None
|
||||||
|
else:
|
||||||
|
parsed_args.laparams = LAParams(
|
||||||
|
line_overlap=parsed_args.line_overlap,
|
||||||
|
char_margin=parsed_args.char_margin,
|
||||||
|
line_margin=parsed_args.line_margin,
|
||||||
|
word_margin=parsed_args.word_margin,
|
||||||
|
boxes_flow=parsed_args.boxes_flow,
|
||||||
|
detect_vertical=parsed_args.detect_vertical,
|
||||||
|
all_texts=parsed_args.all_texts,
|
||||||
|
)
|
||||||
|
|
||||||
|
if parsed_args.page_numbers:
|
||||||
|
parsed_args.page_numbers = {x - 1 for x in parsed_args.page_numbers}
|
||||||
|
|
||||||
|
if parsed_args.pagenos:
|
||||||
|
parsed_args.page_numbers = {int(x) - 1 for x in parsed_args.pagenos.split(",")}
|
||||||
|
|
||||||
|
if parsed_args.output_type == "text" and parsed_args.outfile != "-":
|
||||||
|
for override, alttype in OUTPUT_TYPES:
|
||||||
|
if parsed_args.outfile.endswith(override):
|
||||||
|
parsed_args.output_type = alttype
|
||||||
|
|
||||||
|
return parsed_args
|
||||||
|
|
||||||
|
|
||||||
|
def main(args: Optional[List[str]] = None) -> int:
|
||||||
|
parsed_args = parse_args(args)
|
||||||
|
outfp = extract_text(**vars(parsed_args))
|
||||||
|
outfp.close()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
8
bin/tqdm
Executable file
8
bin/tqdm
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/home/johannes/code/transport-accessibility/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from tqdm.cli import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(main())
|
||||||
1
delete_db.sql
Normal file
1
delete_db.sql
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
DROP DATABASE transport_accessibility;
|
||||||
28504
example.sql
Normal file
28504
example.sql
Normal file
File diff suppressed because it is too large
Load Diff
3
setup_db.sql
Normal file
3
setup_db.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
CREATE DATABASE IF NOT EXISTS transport_accessibility;
|
||||||
|
CREATE USER IF NOT EXISTS 'transport_accessibility'@'localhost' IDENTIFIED BY 'L8AClYIsC55SEAWTgYopD';
|
||||||
|
GRANT ALL PRIVILEGES ON transport_accessibility.* TO 'transport_accessibility'@'localhost';
|
||||||
291
transport_accessibility/pt_map/bridge.py
Normal file
291
transport_accessibility/pt_map/bridge.py
Normal file
|
|
@ -0,0 +1,291 @@
|
||||||
|
import pt_map.gtfs
|
||||||
|
import pt_map.models
|
||||||
|
import pandas as pd
|
||||||
|
from pattern.text.en import singularize
|
||||||
|
import math
|
||||||
|
import numbers
|
||||||
|
import email.utils
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import django.db.models
|
||||||
|
|
||||||
|
gtfs_schema = {
|
||||||
|
"agency": [
|
||||||
|
"agency_id",
|
||||||
|
"agency_name",
|
||||||
|
"agency_url",
|
||||||
|
"agency_timezone",
|
||||||
|
"agency_lang",
|
||||||
|
"agency_phone",
|
||||||
|
"agency_email",
|
||||||
|
"agency_fare_url"
|
||||||
|
],
|
||||||
|
"stops": [
|
||||||
|
"stop_id",
|
||||||
|
"stop_code",
|
||||||
|
"stop_name",
|
||||||
|
"stop_desc",
|
||||||
|
"stop_lat",
|
||||||
|
"stop_lon",
|
||||||
|
"zone_id",
|
||||||
|
"stop_url",
|
||||||
|
"location_type",
|
||||||
|
"parent_station",
|
||||||
|
"stop_timezone",
|
||||||
|
"wheelchair_boarding",
|
||||||
|
"level_id",
|
||||||
|
"platform_code"
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
"route_id",
|
||||||
|
"agency_id",
|
||||||
|
"route_short_name",
|
||||||
|
"route_long_name",
|
||||||
|
"route_desc",
|
||||||
|
"route_type",
|
||||||
|
"route_url",
|
||||||
|
"route_color",
|
||||||
|
"route_text_color",
|
||||||
|
"route_sort_order",
|
||||||
|
"continuous_pickup",
|
||||||
|
"continuous_drop_off"
|
||||||
|
],
|
||||||
|
"trips": [
|
||||||
|
"trip_id",
|
||||||
|
"route_id",
|
||||||
|
"service_id",
|
||||||
|
"trip_headsign",
|
||||||
|
"trip_short_name",
|
||||||
|
"direction_id",
|
||||||
|
"block_id",
|
||||||
|
"shape_id",
|
||||||
|
"wheelchair_accessible",
|
||||||
|
"bikes_allowed"
|
||||||
|
],
|
||||||
|
"stop_times": [
|
||||||
|
"trip_id",
|
||||||
|
"arrival_time",
|
||||||
|
"departure_time",
|
||||||
|
"stop_id",
|
||||||
|
"stop_sequence",
|
||||||
|
"stop_headsign",
|
||||||
|
"pickup_type",
|
||||||
|
"drop_off_type",
|
||||||
|
"shape_dist_traveled",
|
||||||
|
"timepoint"
|
||||||
|
],
|
||||||
|
"calendar": [
|
||||||
|
"service_id",
|
||||||
|
"monday",
|
||||||
|
"tuesday",
|
||||||
|
"wednesday",
|
||||||
|
"thursday",
|
||||||
|
"friday",
|
||||||
|
"saturday",
|
||||||
|
"sunday",
|
||||||
|
"start_date",
|
||||||
|
"end_date"
|
||||||
|
],
|
||||||
|
"calendar_dates": [
|
||||||
|
"service_id",
|
||||||
|
"date",
|
||||||
|
"exception_type"
|
||||||
|
],
|
||||||
|
"fare_attributes": [
|
||||||
|
"fare_id",
|
||||||
|
"price",
|
||||||
|
"currency_type",
|
||||||
|
"payment_method",
|
||||||
|
"transfers",
|
||||||
|
"transfer_duration"
|
||||||
|
],
|
||||||
|
"fare_rules": [
|
||||||
|
"fare_id",
|
||||||
|
"route_id",
|
||||||
|
"origin_id",
|
||||||
|
"destination_id",
|
||||||
|
"contains_id"
|
||||||
|
],
|
||||||
|
"timeframes": [
|
||||||
|
"timeframe_id",
|
||||||
|
"start_time",
|
||||||
|
"end_time",
|
||||||
|
"headway_sec",
|
||||||
|
"exact_times"
|
||||||
|
],
|
||||||
|
"fare_media": [
|
||||||
|
"media_id",
|
||||||
|
"agency_id",
|
||||||
|
"fare_id",
|
||||||
|
"seat_type",
|
||||||
|
"price"
|
||||||
|
],
|
||||||
|
"fare_products": [
|
||||||
|
"product_id",
|
||||||
|
"agency_id",
|
||||||
|
"product_type",
|
||||||
|
"fare_id",
|
||||||
|
"product_name",
|
||||||
|
"short_name",
|
||||||
|
"description",
|
||||||
|
"duration",
|
||||||
|
"transfers"
|
||||||
|
],
|
||||||
|
"fare_leg_rules": [
|
||||||
|
"fare_id",
|
||||||
|
"route_id",
|
||||||
|
"origin_id",
|
||||||
|
"destination_id",
|
||||||
|
"contains_id"
|
||||||
|
],
|
||||||
|
"fare_transfer_rules": [
|
||||||
|
"from_fare_id",
|
||||||
|
"to_fare_id",
|
||||||
|
"transfer_type",
|
||||||
|
"min_transfer_time"
|
||||||
|
],
|
||||||
|
"areas": [
|
||||||
|
"area_id",
|
||||||
|
"area_name",
|
||||||
|
"area_description"
|
||||||
|
],
|
||||||
|
"stop_areas": [
|
||||||
|
"stop_area_id",
|
||||||
|
"stop_id",
|
||||||
|
"area_id",
|
||||||
|
"location_type",
|
||||||
|
"parent_station",
|
||||||
|
"fare_zone_id"
|
||||||
|
],
|
||||||
|
"networks": [
|
||||||
|
"network_id",
|
||||||
|
"network_name",
|
||||||
|
"network_description"
|
||||||
|
],
|
||||||
|
"route_networks": [
|
||||||
|
"route_id",
|
||||||
|
"network_id"
|
||||||
|
],
|
||||||
|
"shapes": [
|
||||||
|
"shape_id",
|
||||||
|
"shape_pt_lat",
|
||||||
|
"shape_pt_lon",
|
||||||
|
"shape_pt_sequence",
|
||||||
|
"shape_dist_traveled"
|
||||||
|
],
|
||||||
|
"frequencies": [
|
||||||
|
"trip_id",
|
||||||
|
"start_time",
|
||||||
|
"end_time",
|
||||||
|
"headway_secs",
|
||||||
|
"exact_times"
|
||||||
|
],
|
||||||
|
"transfers": [
|
||||||
|
"from_stop_id",
|
||||||
|
"to_stop_id",
|
||||||
|
"transfer_type",
|
||||||
|
"min_transfer_time"
|
||||||
|
],
|
||||||
|
"pathways": [
|
||||||
|
"pathway_id",
|
||||||
|
"from_stop_id",
|
||||||
|
"to_stop_id",
|
||||||
|
"pathway_mode",
|
||||||
|
"is_bidirectional",
|
||||||
|
"length",
|
||||||
|
"traversal_time",
|
||||||
|
"stair_count",
|
||||||
|
"max_slope",
|
||||||
|
"min_width",
|
||||||
|
"signposted_as",
|
||||||
|
"reversed_signposted_as"
|
||||||
|
],
|
||||||
|
"levels": [
|
||||||
|
"level_id",
|
||||||
|
"level_index",
|
||||||
|
"level_name"
|
||||||
|
],
|
||||||
|
"location_groups": [
|
||||||
|
"location_group_id",
|
||||||
|
"location_group_name"
|
||||||
|
],
|
||||||
|
"location_group_stops": [
|
||||||
|
"location_group_id",
|
||||||
|
"stop_id"
|
||||||
|
],
|
||||||
|
"locations_geojson": [
|
||||||
|
"type",
|
||||||
|
"features"
|
||||||
|
],
|
||||||
|
"booking_rules": [
|
||||||
|
"rule_id",
|
||||||
|
"stop_id",
|
||||||
|
"rule_type",
|
||||||
|
"booking_url",
|
||||||
|
"admission_rules",
|
||||||
|
"admission_requirements"
|
||||||
|
],
|
||||||
|
"translations": [
|
||||||
|
"table_name",
|
||||||
|
"field_name",
|
||||||
|
"language",
|
||||||
|
"translation"
|
||||||
|
],
|
||||||
|
"feed_info": [
|
||||||
|
"feed_publisher_name",
|
||||||
|
"feed_publisher_url",
|
||||||
|
"feed_lang",
|
||||||
|
"default_lang",
|
||||||
|
"feed_start_date",
|
||||||
|
"feed_end_date",
|
||||||
|
"feed_version",
|
||||||
|
"feed_contact_email",
|
||||||
|
"feed_contact_url"
|
||||||
|
],
|
||||||
|
"attributions": [
|
||||||
|
"attribution_id",
|
||||||
|
"organization_name",
|
||||||
|
"is_producer"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_camel_case(s: str):
|
||||||
|
return ''.join(word.capitalize() for word in s.split('_'))
|
||||||
|
|
||||||
|
def standardize_time(time_str):
|
||||||
|
date_str = f"Jan 19, 1999 {time_str}"
|
||||||
|
ntuple=email.utils.parsedate(date_str)
|
||||||
|
timestamp=time.mktime(ntuple)
|
||||||
|
date=datetime.datetime.fromtimestamp(timestamp)
|
||||||
|
return date.strftime('%H:%M:%S')
|
||||||
|
|
||||||
|
|
||||||
|
def is_NaN(v):
|
||||||
|
return (isinstance(v, str) and v.lower() == "nan") or (isinstance(v, numbers.Number) and math.isnan(v))
|
||||||
|
|
||||||
|
def stdz(v, m, f):
|
||||||
|
if m._meta.get_field(f).get_internal_type() == 'DateField':
|
||||||
|
return str(v)
|
||||||
|
if m._meta.get_field(f).get_internal_type() == 'TimeField':
|
||||||
|
return standardize_time(v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def gtfs_to_db(g: pt_map.gtfs.GTFS):
|
||||||
|
for k,v in gtfs_schema.items():
|
||||||
|
name = to_camel_case(singularize(k))
|
||||||
|
m = getattr(pt_map.models, name)
|
||||||
|
df = getattr(g, k).data
|
||||||
|
print("\n\n\n\n")
|
||||||
|
print(name)
|
||||||
|
print("#############################################################################################")
|
||||||
|
if not df.empty:
|
||||||
|
for _, row in df.iterrows():
|
||||||
|
defaults = {field: stdz(row.get(field), m, field) for field in v if row.get(field) and not is_NaN(row[field])}
|
||||||
|
kw_args = {v[0]: row[v[0]]}
|
||||||
|
m.objects.update_or_create(
|
||||||
|
defaults = defaults,
|
||||||
|
**kw_args,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -13,12 +13,29 @@ class GTFS:
|
||||||
self.calendar_dates = self.CalendarDates(self.folder_path)
|
self.calendar_dates = self.CalendarDates(self.folder_path)
|
||||||
self.fare_attributes = self.FareAttributes(self.folder_path)
|
self.fare_attributes = self.FareAttributes(self.folder_path)
|
||||||
self.fare_rules = self.FareRules(self.folder_path)
|
self.fare_rules = self.FareRules(self.folder_path)
|
||||||
|
self.timeframes = self.Timeframes(self.folder_path)
|
||||||
|
self.fare_media = self.FareMedia(self.folder_path)
|
||||||
|
self.fare_products = self.FareProducts(self.folder_path)
|
||||||
|
self.fare_leg_rules = self.FareLegRules(self.folder_path)
|
||||||
|
self.fare_transfer_rules = self.FareTransferRules(self.folder_path)
|
||||||
|
self.areas = self.Areas(self.folder_path)
|
||||||
|
self.stop_areas = self.StopAreas(self.folder_path)
|
||||||
|
self.networks = self.Networks(self.folder_path)
|
||||||
|
self.route_networks = self.RouteNetworks(self.folder_path)
|
||||||
self.shapes = self.Shapes(self.folder_path)
|
self.shapes = self.Shapes(self.folder_path)
|
||||||
self.frequencies = self.Frequencies(self.folder_path)
|
self.frequencies = self.Frequencies(self.folder_path)
|
||||||
self.transfers = self.Transfers(self.folder_path)
|
self.transfers = self.Transfers(self.folder_path)
|
||||||
|
self.pathways = self.Pathways(self.folder_path)
|
||||||
|
self.levels = self.Levels(self.folder_path)
|
||||||
|
self.location_groups = self.LocationGroups(self.folder_path)
|
||||||
|
self.location_group_stops = self.LocationGroupStops(self.folder_path)
|
||||||
|
self.locations_geojson = self.LocationsGeojson(self.folder_path)
|
||||||
|
self.booking_rules = self.BookingRules(self.folder_path)
|
||||||
|
self.translations = self.Translations(self.folder_path)
|
||||||
self.feed_info = self.FeedInfo(self.folder_path)
|
self.feed_info = self.FeedInfo(self.folder_path)
|
||||||
|
self.attributions = self.Attributions(self.folder_path)
|
||||||
self.errors = []
|
self.errors = []
|
||||||
|
|
||||||
class GTFSFile:
|
class GTFSFile:
|
||||||
def __init__(self, folder_path, file_name):
|
def __init__(self, folder_path, file_name):
|
||||||
self.file_path = f"{folder_path}/{file_name}.txt"
|
self.file_path = f"{folder_path}/{file_name}.txt"
|
||||||
|
|
@ -66,6 +83,42 @@ class GTFS:
|
||||||
def __init__(self, folder_path):
|
def __init__(self, folder_path):
|
||||||
super().__init__(folder_path, 'fare_rules')
|
super().__init__(folder_path, 'fare_rules')
|
||||||
|
|
||||||
|
class Timeframes(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'timeframes')
|
||||||
|
|
||||||
|
class FareMedia(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'fare_media')
|
||||||
|
|
||||||
|
class FareProducts(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'fare_products')
|
||||||
|
|
||||||
|
class FareLegRules(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'fare_leg_rules')
|
||||||
|
|
||||||
|
class FareTransferRules(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'fare_transfer_rules')
|
||||||
|
|
||||||
|
class Areas(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'areas')
|
||||||
|
|
||||||
|
class StopAreas(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'stop_areas')
|
||||||
|
|
||||||
|
class Networks(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'networks')
|
||||||
|
|
||||||
|
class RouteNetworks(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'route_networks')
|
||||||
|
|
||||||
class Shapes(GTFSFile):
|
class Shapes(GTFSFile):
|
||||||
def __init__(self, folder_path):
|
def __init__(self, folder_path):
|
||||||
super().__init__(folder_path, 'shapes')
|
super().__init__(folder_path, 'shapes')
|
||||||
|
|
@ -78,32 +131,73 @@ class GTFS:
|
||||||
def __init__(self, folder_path):
|
def __init__(self, folder_path):
|
||||||
super().__init__(folder_path, 'transfers')
|
super().__init__(folder_path, 'transfers')
|
||||||
|
|
||||||
|
class Pathways(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'pathways')
|
||||||
|
|
||||||
|
class Levels(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'levels')
|
||||||
|
|
||||||
|
class LocationGroups(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'location_groups')
|
||||||
|
|
||||||
|
class LocationGroupStops(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'location_group_stops')
|
||||||
|
|
||||||
|
class LocationsGeojson(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
self.file_path = f"{folder_path}/locations.geojson"
|
||||||
|
if os.path.exists(self.file_path):
|
||||||
|
self.data = self.load_data()
|
||||||
|
else:
|
||||||
|
self.data = pd.DataFrame()
|
||||||
|
|
||||||
|
def load_data(self):
|
||||||
|
try:
|
||||||
|
return pd.read_json(self.file_path)
|
||||||
|
except ValueError:
|
||||||
|
return pd.DataFrame()
|
||||||
|
|
||||||
|
class BookingRules(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'booking_rules')
|
||||||
|
|
||||||
|
class Translations(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'translations')
|
||||||
|
|
||||||
class FeedInfo(GTFSFile):
|
class FeedInfo(GTFSFile):
|
||||||
def __init__(self, folder_path):
|
def __init__(self, folder_path):
|
||||||
super().__init__(folder_path, 'feed_info')
|
super().__init__(folder_path, 'feed_info')
|
||||||
|
|
||||||
|
class Attributions(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'attributions')
|
||||||
|
|
||||||
def get_files(self):
|
def get_files(self):
|
||||||
return [attr for attr in list(set(dir(self)) - set(dir(GTFS))) if isinstance(getattr(self,attr),self.GTFSFile)]
|
return [attr for attr in dir(self) if isinstance(getattr(self, attr), self.GTFSFile)]
|
||||||
|
|
||||||
def get_fields(self, name):
|
def get_fields(self, name):
|
||||||
file = getattr(self, name)
|
file = getattr(self, name)
|
||||||
if not file:
|
if not file:
|
||||||
return None
|
return None
|
||||||
return list(set(dir(file)) - set(dir(GTFSFile)))
|
return list(file.data.columns)
|
||||||
|
|
||||||
def export(self, path, dirname):
|
def export(self, path, dirname):
|
||||||
path = f"{os.path.normpath(path)}/{dirname}"
|
path = f"{os.path.normpath(path)}/{dirname}"
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
os.mkdir(path)
|
os.mkdir(path)
|
||||||
print(self.get_files())
|
|
||||||
for name in self.get_files():
|
for name in self.get_files():
|
||||||
df = getattr(self, name).data
|
df = getattr(self, name).data
|
||||||
fpath = f"{path}/{name}.txt"
|
fpath = f"{path}/{name}.txt"
|
||||||
# print(f"name: {name}")
|
if name == 'locations_geojson':
|
||||||
print(name)
|
fpath = f"{path}/{name}.geojson"
|
||||||
df.to_csv(fpath, index=False)
|
df.to_json(fpath)
|
||||||
|
else:
|
||||||
|
df.to_csv(fpath, index=False)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_agency()
|
self.validate_agency()
|
||||||
|
|
@ -112,6 +206,30 @@ class GTFS:
|
||||||
self.validate_trips()
|
self.validate_trips()
|
||||||
self.validate_stop_times()
|
self.validate_stop_times()
|
||||||
self.validate_calendar()
|
self.validate_calendar()
|
||||||
|
self.validate_calendar_dates()
|
||||||
|
self.validate_fare_attributes()
|
||||||
|
self.validate_fare_rules()
|
||||||
|
self.validate_timeframes()
|
||||||
|
self.validate_fare_media()
|
||||||
|
self.validate_fare_products()
|
||||||
|
self.validate_fare_leg_rules()
|
||||||
|
self.validate_fare_transfer_rules()
|
||||||
|
self.validate_areas()
|
||||||
|
self.validate_stop_areas()
|
||||||
|
self.validate_networks()
|
||||||
|
self.validate_route_networks()
|
||||||
|
self.validate_shapes()
|
||||||
|
self.validate_frequencies()
|
||||||
|
self.validate_transfers()
|
||||||
|
self.validate_pathways()
|
||||||
|
self.validate_levels()
|
||||||
|
self.validate_location_groups()
|
||||||
|
self.validate_location_group_stops()
|
||||||
|
self.validate_locations_geojson()
|
||||||
|
self.validate_booking_rules()
|
||||||
|
self.validate_translations()
|
||||||
|
self.validate_feed_info()
|
||||||
|
self.validate_attributions()
|
||||||
self.validate_cross_references()
|
self.validate_cross_references()
|
||||||
|
|
||||||
if not self.errors:
|
if not self.errors:
|
||||||
|
|
@ -120,54 +238,207 @@ class GTFS:
|
||||||
return self.errors
|
return self.errors
|
||||||
|
|
||||||
def validate_agency(self):
|
def validate_agency(self):
|
||||||
required_fields = ["agency_id", "agency_name", "agency_url", "agency_timezone"]
|
required_fields = ["agency_name", "agency_url", "agency_timezone"]
|
||||||
|
optional_fields = ["agency_id", "agency_lang", "agency_phone", "agency_fare_url", "agency_email"]
|
||||||
self.validate_required_fields(self.agency.data, required_fields, "agency.txt")
|
self.validate_required_fields(self.agency.data, required_fields, "agency.txt")
|
||||||
|
self.validate_optional_fields(self.agency.data, optional_fields, "agency.txt")
|
||||||
|
|
||||||
def validate_stops(self):
|
def validate_stops(self):
|
||||||
required_fields = ["stop_id", "stop_name", "stop_lat", "stop_lon"]
|
required_fields = ["stop_id", "stop_name"]
|
||||||
|
optional_fields = ["stop_code", "stop_desc", "stop_lat", "stop_lon", "zone_id", "stop_url",
|
||||||
|
"location_type", "parent_station", "stop_timezone", "wheelchair_boarding",
|
||||||
|
"level_id", "platform_code"]
|
||||||
self.validate_required_fields(self.stops.data, required_fields, "stops.txt")
|
self.validate_required_fields(self.stops.data, required_fields, "stops.txt")
|
||||||
|
self.validate_optional_fields(self.stops.data, optional_fields, "stops.txt")
|
||||||
self.validate_lat_lon(self.stops.data)
|
self.validate_lat_lon(self.stops.data)
|
||||||
|
|
||||||
def validate_routes(self):
|
def validate_routes(self):
|
||||||
required_fields = ["route_id", "route_short_name", "route_long_name", "route_type"]
|
required_fields = ["route_id", "route_short_name", "route_long_name", "route_type"]
|
||||||
|
optional_fields = ["agency_id", "route_desc", "route_url", "route_color", "route_text_color",
|
||||||
|
"route_sort_order", "continuous_pickup", "continuous_drop_off"]
|
||||||
self.validate_required_fields(self.routes.data, required_fields, "routes.txt")
|
self.validate_required_fields(self.routes.data, required_fields, "routes.txt")
|
||||||
|
self.validate_optional_fields(self.routes.data, optional_fields, "routes.txt")
|
||||||
|
|
||||||
def validate_trips(self):
|
def validate_trips(self):
|
||||||
required_fields = ["route_id", "service_id", "trip_id"]
|
required_fields = ["route_id", "service_id", "trip_id"]
|
||||||
|
optional_fields = ["trip_headsign", "trip_short_name", "direction_id", "block_id", "shape_id",
|
||||||
|
"wheelchair_accessible", "bikes_allowed"]
|
||||||
self.validate_required_fields(self.trips.data, required_fields, "trips.txt")
|
self.validate_required_fields(self.trips.data, required_fields, "trips.txt")
|
||||||
|
self.validate_optional_fields(self.trips.data, optional_fields, "trips.txt")
|
||||||
|
|
||||||
def validate_stop_times(self):
|
def validate_stop_times(self):
|
||||||
required_fields = ["trip_id", "arrival_time", "departure_time", "stop_id", "stop_sequence"]
|
required_fields = ["trip_id", "arrival_time", "departure_time", "stop_id", "stop_sequence"]
|
||||||
|
optional_fields = ["stop_headsign", "pickup_type", "drop_off_type", "shape_dist_traveled",
|
||||||
|
"timepoint"]
|
||||||
self.validate_required_fields(self.stop_times.data, required_fields, "stop_times.txt")
|
self.validate_required_fields(self.stop_times.data, required_fields, "stop_times.txt")
|
||||||
|
self.validate_optional_fields(self.stop_times.data, optional_fields, "stop_times.txt")
|
||||||
|
|
||||||
def validate_calendar(self):
|
def validate_calendar(self):
|
||||||
required_fields = ["service_id", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", "start_date", "end_date"]
|
required_fields = ["service_id", "monday", "tuesday", "wednesday", "thursday", "friday",
|
||||||
|
"saturday", "sunday", "start_date", "end_date"]
|
||||||
self.validate_required_fields(self.calendar.data, required_fields, "calendar.txt")
|
self.validate_required_fields(self.calendar.data, required_fields, "calendar.txt")
|
||||||
|
|
||||||
def validate_required_fields(self, data, required_fields, filename):
|
def validate_calendar_dates(self):
|
||||||
for field in required_fields:
|
required_fields = ["service_id", "date", "exception_type"]
|
||||||
if field not in data.columns:
|
self.validate_required_fields(self.calendar_dates.data, required_fields, "calendar_dates.txt")
|
||||||
self.errors.append(f"Error: {filename} missing required field: {field}")
|
|
||||||
|
|
||||||
def validate_lat_lon(self, data):
|
def validate_fare_attributes(self):
|
||||||
for index, row in data.iterrows():
|
required_fields = ["fare_id", "price", "currency_type", "payment_method", "transfers"]
|
||||||
if not (-90 <= row['stop_lat'] <= 90):
|
optional_fields = ["agency_id", "transfer_duration"]
|
||||||
self.errors.append(f"Error: stops.txt invalid latitude at row {index}: {row['stop_lat']}")
|
self.validate_required_fields(self.fare_attributes.data, required_fields, "fare_attributes.txt")
|
||||||
if not (-180 <= row['stop_lon'] <= 180):
|
self.validate_optional_fields(self.fare_attributes.data, optional_fields, "fare_attributes.txt")
|
||||||
self.errors.append(f"Error: stops.txt invalid longitude at row {index}: {row['stop_lon']}")
|
|
||||||
|
def validate_fare_rules(self):
|
||||||
|
required_fields = ["fare_id"]
|
||||||
|
optional_fields = ["route_id", "origin_id", "destination_id", "contains_id"]
|
||||||
|
self.validate_required_fields(self.fare_rules.data, required_fields, "fare_rules.txt")
|
||||||
|
self.validate_optional_fields(self.fare_rules.data, optional_fields, "fare_rules.txt")
|
||||||
|
|
||||||
|
def validate_timeframes(self):
|
||||||
|
required_fields = ["timeframe_id", "start_time", "end_time"]
|
||||||
|
optional_fields = ["timeframe_name", "timeframe_desc"]
|
||||||
|
self.validate_required_fields(self.timeframes.data, required_fields, "timeframes.txt")
|
||||||
|
self.validate_optional_fields(self.timeframes.data, optional_fields, "timeframes.txt")
|
||||||
|
|
||||||
|
def validate_fare_media(self):
|
||||||
|
required_fields = ["media_id", "media_name", "media_type"]
|
||||||
|
optional_fields = ["media_desc"]
|
||||||
|
self.validate_required_fields(self.fare_media.data, required_fields, "fare_media.txt")
|
||||||
|
self.validate_optional_fields(self.fare_media.data, optional_fields, "fare_media.txt")
|
||||||
|
|
||||||
|
def validate_fare_products(self):
|
||||||
|
required_fields = ["product_id", "product_name", "product_type", "product_price", "currency"]
|
||||||
|
optional_fields = ["product_desc"]
|
||||||
|
self.validate_required_fields(self.fare_products.data, required_fields, "fare_products.txt")
|
||||||
|
self.validate_optional_fields(self.fare_products.data, optional_fields, "fare_products.txt")
|
||||||
|
|
||||||
|
def validate_fare_leg_rules(self):
|
||||||
|
required_fields = ["leg_id", "from_stop_id", "to_stop_id"]
|
||||||
|
optional_fields = ["leg_desc"]
|
||||||
|
self.validate_required_fields(self.fare_leg_rules.data, required_fields, "fare_leg_rules.txt")
|
||||||
|
self.validate_optional_fields(self.fare_leg_rules.data, optional_fields, "fare_leg_rules.txt")
|
||||||
|
|
||||||
|
def validate_fare_transfer_rules(self):
|
||||||
|
required_fields = ["from_leg_id", "to_leg_id", "transfer_type"]
|
||||||
|
optional_fields = ["transfer_time"]
|
||||||
|
self.validate_required_fields(self.fare_transfer_rules.data, required_fields, "fare_transfer_rules.txt")
|
||||||
|
self.validate_optional_fields(self.fare_transfer_rules.data, optional_fields, "fare_transfer_rules.txt")
|
||||||
|
|
||||||
|
def validate_areas(self):
|
||||||
|
required_fields = ["area_id", "area_name"]
|
||||||
|
optional_fields = ["area_desc"]
|
||||||
|
self.validate_required_fields(self.areas.data, required_fields, "areas.txt")
|
||||||
|
self.validate_optional_fields(self.areas.data, optional_fields, "areas.txt")
|
||||||
|
|
||||||
|
def validate_stop_areas(self):
|
||||||
|
required_fields = ["stop_id", "area_id"]
|
||||||
|
optional_fields = []
|
||||||
|
self.validate_required_fields(self.stop_areas.data, required_fields, "stop_areas.txt")
|
||||||
|
self.validate_optional_fields(self.stop_areas.data, optional_fields, "stop_areas.txt")
|
||||||
|
|
||||||
|
def validate_networks(self):
|
||||||
|
required_fields = ["network_id", "network_name"]
|
||||||
|
optional_fields = ["network_desc"]
|
||||||
|
self.validate_required_fields(self.networks.data, required_fields, "networks.txt")
|
||||||
|
self.validate_optional_fields(self.networks.data, optional_fields, "networks.txt")
|
||||||
|
|
||||||
|
def validate_route_networks(self):
|
||||||
|
required_fields = ["route_id", "network_id"]
|
||||||
|
optional_fields = []
|
||||||
|
self.validate_required_fields(self.route_networks.data, required_fields, "route_networks.txt")
|
||||||
|
self.validate_optional_fields(self.route_networks.data, optional_fields, "route_networks.txt")
|
||||||
|
|
||||||
|
def validate_shapes(self):
|
||||||
|
required_fields = ["shape_id", "shape_pt_lat", "shape_pt_lon", "shape_pt_sequence"]
|
||||||
|
optional_fields = ["shape_dist_traveled"]
|
||||||
|
self.validate_required_fields(self.shapes.data, required_fields, "shapes.txt")
|
||||||
|
self.validate_optional_fields(self.shapes.data, optional_fields, "shapes.txt")
|
||||||
|
|
||||||
|
def validate_frequencies(self):
|
||||||
|
required_fields = ["trip_id", "start_time", "end_time", "headway_secs"]
|
||||||
|
optional_fields = ["exact_times"]
|
||||||
|
self.validate_required_fields(self.frequencies.data, required_fields, "frequencies.txt")
|
||||||
|
self.validate_optional_fields(self.frequencies.data, optional_fields, "frequencies.txt")
|
||||||
|
|
||||||
|
def validate_transfers(self):
|
||||||
|
required_fields = ["from_stop_id", "to_stop_id", "transfer_type"]
|
||||||
|
optional_fields = ["min_transfer_time"]
|
||||||
|
self.validate_required_fields(self.transfers.data, required_fields, "transfers.txt")
|
||||||
|
self.validate_optional_fields(self.transfers.data, optional_fields, "transfers.txt")
|
||||||
|
|
||||||
|
def validate_pathways(self):
|
||||||
|
required_fields = ["pathway_id", "from_stop_id", "to_stop_id", "pathway_mode", "is_bidirectional"]
|
||||||
|
optional_fields = ["length", "traversal_time", "stair_count", "max_slope", "min_width", "signposted_as", "reversed_signposted_as"]
|
||||||
|
self.validate_required_fields(self.pathways.data, required_fields, "pathways.txt")
|
||||||
|
self.validate_optional_fields(self.pathways.data, optional_fields, "pathways.txt")
|
||||||
|
|
||||||
|
def validate_levels(self):
|
||||||
|
required_fields = ["level_id", "level_index"]
|
||||||
|
optional_fields = ["level_name"]
|
||||||
|
self.validate_required_fields(self.levels.data, required_fields, "levels.txt")
|
||||||
|
self.validate_optional_fields(self.levels.data, optional_fields, "levels.txt")
|
||||||
|
|
||||||
|
def validate_location_groups(self):
|
||||||
|
required_fields = ["location_group_id", "location_group_name"]
|
||||||
|
optional_fields = ["location_group_desc"]
|
||||||
|
self.validate_required_fields(self.location_groups.data, required_fields, "location_groups.txt")
|
||||||
|
self.validate_optional_fields(self.location_groups.data, optional_fields, "location_groups.txt")
|
||||||
|
|
||||||
|
def validate_location_group_stops(self):
|
||||||
|
required_fields = ["location_group_id", "stop_id"]
|
||||||
|
optional_fields = []
|
||||||
|
self.validate_required_fields(self.location_group_stops.data, required_fields, "location_group_stops.txt")
|
||||||
|
self.validate_optional_fields(self.location_group_stops.data, optional_fields, "location_group_stops.txt")
|
||||||
|
|
||||||
|
def validate_locations_geojson(self):
|
||||||
|
required_fields = ["type", "features"]
|
||||||
|
optional_fields = []
|
||||||
|
self.validate_required_fields(self.locations_geojson.data, required_fields, "locations.geojson")
|
||||||
|
self.validate_optional_fields(self.locations_geojson.data, optional_fields, "locations.geojson")
|
||||||
|
|
||||||
|
def validate_booking_rules(self):
|
||||||
|
required_fields = ["booking_rule_id"]
|
||||||
|
optional_fields = ["booking_rule_name", "booking_rule_desc"]
|
||||||
|
self.validate_required_fields(self.booking_rules.data, required_fields, "booking_rules.txt")
|
||||||
|
self.validate_optional_fields(self.booking_rules.data, optional_fields, "booking_rules.txt")
|
||||||
|
|
||||||
|
def validate_translations(self):
|
||||||
|
required_fields = ["table_name", "field_name", "language", "translation"]
|
||||||
|
optional_fields = ["record_id", "record_sub_id", "field_value"]
|
||||||
|
self.validate_required_fields(self.translations.data, required_fields, "translations.txt")
|
||||||
|
self.validate_optional_fields(self.translations.data, optional_fields, "translations.txt")
|
||||||
|
|
||||||
|
def validate_feed_info(self):
|
||||||
|
required_fields = ["feed_publisher_name", "feed_publisher_url", "feed_lang"]
|
||||||
|
optional_fields = ["feed_start_date", "feed_end_date", "feed_version"]
|
||||||
|
self.validate_required_fields(self.feed_info.data, required_fields, "feed_info.txt")
|
||||||
|
self.validate_optional_fields(self.feed_info.data, optional_fields, "feed_info.txt")
|
||||||
|
|
||||||
|
def validate_attributions(self):
|
||||||
|
required_fields = ["attribution_id"]
|
||||||
|
optional_fields = ["agency_id", "route_id", "trip_id", "organization_name", "is_producer", "is_operator", "is_authority", "attribution_url", "attribution_email", "attribution_phone"]
|
||||||
|
self.validate_required_fields(self.attributions.data, required_fields, "attributions.txt")
|
||||||
|
self.validate_optional_fields(self.attributions.data, optional_fields, "attributions.txt")
|
||||||
|
|
||||||
|
def validate_required_fields(self, df, required_fields, file_name):
|
||||||
|
missing_fields = set(required_fields) - set(df.columns)
|
||||||
|
if missing_fields:
|
||||||
|
self.errors.append(f"{file_name} is missing required fields: {missing_fields}")
|
||||||
|
|
||||||
|
def validate_optional_fields(self, df, optional_fields, file_name):
|
||||||
|
unexpected_fields = set(df.columns) - set(optional_fields) - set(df.columns)
|
||||||
|
if unexpected_fields:
|
||||||
|
self.errors.append(f"{file_name} has unexpected fields: {unexpected_fields}")
|
||||||
|
|
||||||
|
def validate_lat_lon(self, df):
|
||||||
|
if 'stop_lat' in df.columns and 'stop_lon' in df.columns:
|
||||||
|
if df[['stop_lat', 'stop_lon']].isnull().any().any():
|
||||||
|
self.errors.append(f"stops.txt has missing lat/lon values.")
|
||||||
|
|
||||||
def validate_cross_references(self):
|
def validate_cross_references(self):
|
||||||
# Validate that trip_ids in stop_times.txt exist in trips.txt
|
# Example: Validate that all stop_ids in stop_times.txt exist in stops.txt
|
||||||
stop_times_trip_ids = set(self.stop_times.data['trip_id'])
|
if not self.stop_times.data.empty and not self.stops.data.empty:
|
||||||
trips_trip_ids = set(self.trips.data['trip_id'])
|
invalid_stops = set(self.stop_times.data['stop_id']) - set(self.stops.data['stop_id'])
|
||||||
missing_trip_ids = stop_times_trip_ids - trips_trip_ids
|
if invalid_stops:
|
||||||
for trip_id in missing_trip_ids:
|
self.errors.append(f"stop_times.txt has invalid stop_ids: {invalid_stops}")
|
||||||
self.errors.append(f"Error: trip_id {trip_id} in stop_times.txt does not exist in trips.txt")
|
|
||||||
|
|
||||||
# Validate that stop_ids in stop_times.txt exist in stops.txt
|
|
||||||
stop_times_stop_ids = set(self.stop_times.data['stop_id'])
|
|
||||||
stops_stop_ids = set(self.stops.data['stop_id'])
|
|
||||||
missing_stop_ids = stop_times_stop_ids - stops_stop_ids
|
|
||||||
for stop_id in missing_stop_ids:
|
|
||||||
self.errors.append(f"Error: stop_id {stop_id} in stop_times.txt does not exist in stops.txt")
|
|
||||||
|
|
||||||
|
|
|
||||||
173
transport_accessibility/pt_map/gtfs.py.bck
Normal file
173
transport_accessibility/pt_map/gtfs.py.bck
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
|
||||||
|
class GTFS:
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
self.folder_path = folder_path
|
||||||
|
self.agency = self.Agency(self.folder_path)
|
||||||
|
self.stops = self.Stops(self.folder_path)
|
||||||
|
self.routes = self.Routes(self.folder_path)
|
||||||
|
self.trips = self.Trips(self.folder_path)
|
||||||
|
self.stop_times = self.StopTimes(self.folder_path)
|
||||||
|
self.calendar = self.Calendar(self.folder_path)
|
||||||
|
self.calendar_dates = self.CalendarDates(self.folder_path)
|
||||||
|
self.fare_attributes = self.FareAttributes(self.folder_path)
|
||||||
|
self.fare_rules = self.FareRules(self.folder_path)
|
||||||
|
self.shapes = self.Shapes(self.folder_path)
|
||||||
|
self.frequencies = self.Frequencies(self.folder_path)
|
||||||
|
self.transfers = self.Transfers(self.folder_path)
|
||||||
|
self.feed_info = self.FeedInfo(self.folder_path)
|
||||||
|
self.errors = []
|
||||||
|
|
||||||
|
class GTFSFile:
|
||||||
|
def __init__(self, folder_path, file_name):
|
||||||
|
self.file_path = f"{folder_path}/{file_name}.txt"
|
||||||
|
self.data = self.load_data()
|
||||||
|
|
||||||
|
def load_data(self):
|
||||||
|
try:
|
||||||
|
return pd.read_csv(self.file_path)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return pd.DataFrame()
|
||||||
|
|
||||||
|
class Agency(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'agency')
|
||||||
|
|
||||||
|
class Stops(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'stops')
|
||||||
|
|
||||||
|
class Routes(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'routes')
|
||||||
|
|
||||||
|
class Trips(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'trips')
|
||||||
|
|
||||||
|
class StopTimes(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'stop_times')
|
||||||
|
|
||||||
|
class Calendar(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'calendar')
|
||||||
|
|
||||||
|
class CalendarDates(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'calendar_dates')
|
||||||
|
|
||||||
|
class FareAttributes(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'fare_attributes')
|
||||||
|
|
||||||
|
class FareRules(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'fare_rules')
|
||||||
|
|
||||||
|
class Shapes(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'shapes')
|
||||||
|
|
||||||
|
class Frequencies(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'frequencies')
|
||||||
|
|
||||||
|
class Transfers(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'transfers')
|
||||||
|
|
||||||
|
class FeedInfo(GTFSFile):
|
||||||
|
def __init__(self, folder_path):
|
||||||
|
super().__init__(folder_path, 'feed_info')
|
||||||
|
|
||||||
|
def get_files(self):
|
||||||
|
return [attr for attr in list(set(dir(self)) - set(dir(GTFS))) if isinstance(getattr(self,attr),self.GTFSFile)]
|
||||||
|
|
||||||
|
def get_fields(self, name):
|
||||||
|
file = getattr(self, name)
|
||||||
|
if not file:
|
||||||
|
return None
|
||||||
|
return list(set(dir(file)) - set(dir(GTFSFile)))
|
||||||
|
|
||||||
|
def export(self, path, dirname):
|
||||||
|
path = f"{os.path.normpath(path)}/{dirname}"
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.mkdir(path)
|
||||||
|
print(self.get_files())
|
||||||
|
for name in self.get_files():
|
||||||
|
df = getattr(self, name).data
|
||||||
|
fpath = f"{path}/{name}.txt"
|
||||||
|
# print(f"name: {name}")
|
||||||
|
print(name)
|
||||||
|
df.to_csv(fpath, index=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
self.validate_agency()
|
||||||
|
self.validate_stops()
|
||||||
|
self.validate_routes()
|
||||||
|
self.validate_trips()
|
||||||
|
self.validate_stop_times()
|
||||||
|
self.validate_calendar()
|
||||||
|
self.validate_cross_references()
|
||||||
|
|
||||||
|
if not self.errors:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return self.errors
|
||||||
|
|
||||||
|
def validate_agency(self):
|
||||||
|
required_fields = ["agency_id", "agency_name", "agency_url", "agency_timezone"]
|
||||||
|
self.validate_required_fields(self.agency.data, required_fields, "agency.txt")
|
||||||
|
|
||||||
|
def validate_stops(self):
|
||||||
|
required_fields = ["stop_id", "stop_name", "stop_lat", "stop_lon"]
|
||||||
|
self.validate_required_fields(self.stops.data, required_fields, "stops.txt")
|
||||||
|
self.validate_lat_lon(self.stops.data)
|
||||||
|
|
||||||
|
def validate_routes(self):
|
||||||
|
required_fields = ["route_id", "route_short_name", "route_long_name", "route_type"]
|
||||||
|
self.validate_required_fields(self.routes.data, required_fields, "routes.txt")
|
||||||
|
|
||||||
|
def validate_trips(self):
|
||||||
|
required_fields = ["route_id", "service_id", "trip_id"]
|
||||||
|
self.validate_required_fields(self.trips.data, required_fields, "trips.txt")
|
||||||
|
|
||||||
|
def validate_stop_times(self):
|
||||||
|
required_fields = ["trip_id", "arrival_time", "departure_time", "stop_id", "stop_sequence"]
|
||||||
|
self.validate_required_fields(self.stop_times.data, required_fields, "stop_times.txt")
|
||||||
|
|
||||||
|
def validate_calendar(self):
|
||||||
|
required_fields = ["service_id", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", "start_date", "end_date"]
|
||||||
|
self.validate_required_fields(self.calendar.data, required_fields, "calendar.txt")
|
||||||
|
|
||||||
|
def validate_required_fields(self, data, required_fields, filename):
|
||||||
|
for field in required_fields:
|
||||||
|
if field not in data.columns:
|
||||||
|
self.errors.append(f"Error: {filename} missing required field: {field}")
|
||||||
|
|
||||||
|
def validate_lat_lon(self, data):
|
||||||
|
for index, row in data.iterrows():
|
||||||
|
if not (-90 <= row['stop_lat'] <= 90):
|
||||||
|
self.errors.append(f"Error: stops.txt invalid latitude at row {index}: {row['stop_lat']}")
|
||||||
|
if not (-180 <= row['stop_lon'] <= 180):
|
||||||
|
self.errors.append(f"Error: stops.txt invalid longitude at row {index}: {row['stop_lon']}")
|
||||||
|
|
||||||
|
def validate_cross_references(self):
|
||||||
|
# Validate that trip_ids in stop_times.txt exist in trips.txt
|
||||||
|
stop_times_trip_ids = set(self.stop_times.data['trip_id'])
|
||||||
|
trips_trip_ids = set(self.trips.data['trip_id'])
|
||||||
|
missing_trip_ids = stop_times_trip_ids - trips_trip_ids
|
||||||
|
for trip_id in missing_trip_ids:
|
||||||
|
self.errors.append(f"Error: trip_id {trip_id} in stop_times.txt does not exist in trips.txt")
|
||||||
|
|
||||||
|
# Validate that stop_ids in stop_times.txt exist in stops.txt
|
||||||
|
stop_times_stop_ids = set(self.stop_times.data['stop_id'])
|
||||||
|
stops_stop_ids = set(self.stops.data['stop_id'])
|
||||||
|
missing_stop_ids = stop_times_stop_ids - stops_stop_ids
|
||||||
|
for stop_id in missing_stop_ids:
|
||||||
|
self.errors.append(f"Error: stop_id {stop_id} in stop_times.txt does not exist in stops.txt")
|
||||||
|
|
||||||
|
|
@ -0,0 +1,205 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-06-02 11:40
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pt_map', '0004_alter_agency_agency_id_alter_calendar_service_id_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Level',
|
||||||
|
fields=[
|
||||||
|
('level_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('level_index', models.FloatField()),
|
||||||
|
('level_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='stop',
|
||||||
|
name='tts_stop_name',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='agency',
|
||||||
|
name='agency_lang',
|
||||||
|
field=models.CharField(blank=True, max_length=2, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='agency',
|
||||||
|
name='agency_timezone',
|
||||||
|
field=models.CharField(default=django.utils.timezone.now, max_length=255),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='feedinfo',
|
||||||
|
name='default_lang',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='feedinfo',
|
||||||
|
name='feed_contact_email',
|
||||||
|
field=models.EmailField(blank=True, max_length=254, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='feedinfo',
|
||||||
|
name='feed_contact_url',
|
||||||
|
field=models.URLField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='route',
|
||||||
|
name='continuous_drop_off',
|
||||||
|
field=models.IntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='route',
|
||||||
|
name='continuous_pickup',
|
||||||
|
field=models.IntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='route',
|
||||||
|
name='route_sort_order',
|
||||||
|
field=models.IntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='agency',
|
||||||
|
name='agency_email',
|
||||||
|
field=models.EmailField(blank=True, max_length=254, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='agency',
|
||||||
|
name='agency_fare_url',
|
||||||
|
field=models.URLField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='agency',
|
||||||
|
name='agency_id',
|
||||||
|
field=models.CharField(max_length=255, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='agency',
|
||||||
|
name='agency_name',
|
||||||
|
field=models.CharField(max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='agency',
|
||||||
|
name='agency_phone',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='calendar',
|
||||||
|
name='service_id',
|
||||||
|
field=models.CharField(max_length=255, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='calendardate',
|
||||||
|
name='service_id',
|
||||||
|
field=models.CharField(max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='fareattribute',
|
||||||
|
name='currency_type',
|
||||||
|
field=models.CharField(max_length=3),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='fareattribute',
|
||||||
|
name='fare_id',
|
||||||
|
field=models.CharField(max_length=255, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='agency',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pt_map.agency'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='route_id',
|
||||||
|
field=models.CharField(max_length=255, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='route_long_name',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='route_short_name',
|
||||||
|
field=models.CharField(max_length=50),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stop',
|
||||||
|
name='platform_code',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stop',
|
||||||
|
name='stop_code',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stop',
|
||||||
|
name='stop_desc',
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stop',
|
||||||
|
name='stop_id',
|
||||||
|
field=models.CharField(max_length=255, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stop',
|
||||||
|
name='stop_lat',
|
||||||
|
field=models.FloatField(),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stop',
|
||||||
|
name='stop_lon',
|
||||||
|
field=models.FloatField(),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stop',
|
||||||
|
name='stop_name',
|
||||||
|
field=models.CharField(max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stop',
|
||||||
|
name='zone_id',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stoptime',
|
||||||
|
name='arrival_time',
|
||||||
|
field=models.TimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stoptime',
|
||||||
|
name='departure_time',
|
||||||
|
field=models.TimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='trip',
|
||||||
|
name='trip_id',
|
||||||
|
field=models.CharField(max_length=255, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Pathway',
|
||||||
|
fields=[
|
||||||
|
('pathway_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('pathway_mode', models.IntegerField()),
|
||||||
|
('is_bidirectional', models.IntegerField()),
|
||||||
|
('length', models.FloatField(blank=True, null=True)),
|
||||||
|
('traversal_time', models.IntegerField(blank=True, null=True)),
|
||||||
|
('stair_count', models.IntegerField(blank=True, null=True)),
|
||||||
|
('max_slope', models.FloatField(blank=True, null=True)),
|
||||||
|
('min_width', models.FloatField(blank=True, null=True)),
|
||||||
|
('signposted_as', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('reversed_signposted_as', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('from_stop', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pathways_from', to='pt_map.stop')),
|
||||||
|
('to_stop', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pathways_to', to='pt_map.stop')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-06-02 12:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pt_map', '0005_level_remove_stop_tts_stop_name_agency_agency_lang_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='route',
|
||||||
|
name='route_type',
|
||||||
|
field=models.IntegerField(default=0),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-06-02 16:56
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pt_map', '0006_alter_route_route_type'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Location',
|
||||||
|
fields=[
|
||||||
|
('location_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('location_name', models.CharField(max_length=255)),
|
||||||
|
('location_lat', models.FloatField()),
|
||||||
|
('location_lon', models.FloatField()),
|
||||||
|
('location_type', models.CharField(max_length=255)),
|
||||||
|
('parent_location_id', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('wheelchair_boarding', models.BooleanField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Translation',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('table_name', models.CharField(max_length=255)),
|
||||||
|
('field_name', models.CharField(max_length=255)),
|
||||||
|
('language', models.CharField(max_length=2)),
|
||||||
|
('translation', models.TextField()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Attribution',
|
||||||
|
fields=[
|
||||||
|
('attribution_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('attribution_name', models.CharField(max_length=255)),
|
||||||
|
('attribution_url', models.URLField()),
|
||||||
|
('attribution_email', models.EmailField(blank=True, max_length=254, null=True)),
|
||||||
|
('attribution_phone', models.CharField(blank=True, max_length=50, null=True)),
|
||||||
|
('agency', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pt_map.agency')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='BookingRule',
|
||||||
|
fields=[
|
||||||
|
('booking_rule_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('start_time', models.TimeField(blank=True, null=True)),
|
||||||
|
('end_time', models.TimeField(blank=True, null=True)),
|
||||||
|
('booking_type', models.CharField(max_length=255)),
|
||||||
|
('rule_criteria', models.TextField(blank=True, null=True)),
|
||||||
|
('booking_rule_instructions', models.TextField(blank=True, null=True)),
|
||||||
|
('trip', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pt_map.trip')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-06-02 17:04
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pt_map', '0007_location_translation_attribution_bookingrule'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Area',
|
||||||
|
fields=[
|
||||||
|
('area_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('area_name', models.CharField(max_length=255)),
|
||||||
|
('area_description', models.TextField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FareLegRule',
|
||||||
|
fields=[
|
||||||
|
('fare_leg_rule_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('fare_leg_rule_name', models.CharField(max_length=255)),
|
||||||
|
('fare_leg_rule_description', models.TextField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FareMedia',
|
||||||
|
fields=[
|
||||||
|
('fare_media_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('fare_media_name', models.CharField(max_length=255)),
|
||||||
|
('fare_media_description', models.TextField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FareProduct',
|
||||||
|
fields=[
|
||||||
|
('fare_product_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('fare_product_name', models.CharField(max_length=255)),
|
||||||
|
('fare_product_description', models.TextField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FareTransferRule',
|
||||||
|
fields=[
|
||||||
|
('fare_transfer_rule_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('fare_transfer_rule_name', models.CharField(max_length=255)),
|
||||||
|
('fare_transfer_rule_description', models.TextField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='LocationGroup',
|
||||||
|
fields=[
|
||||||
|
('location_group_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('location_group_name', models.CharField(max_length=255)),
|
||||||
|
('location_group_type', models.CharField(max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Network',
|
||||||
|
fields=[
|
||||||
|
('network_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('network_name', models.CharField(max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RouteNetwork',
|
||||||
|
fields=[
|
||||||
|
('route_network_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('route_network_name', models.CharField(max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='StopArea',
|
||||||
|
fields=[
|
||||||
|
('stop_area_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('stop_area_name', models.CharField(max_length=255)),
|
||||||
|
('stop_area_description', models.TextField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TimeFrame',
|
||||||
|
fields=[
|
||||||
|
('time_frame_id', models.CharField(max_length=255, primary_key=True, serialize=False)),
|
||||||
|
('start_date', models.DateField()),
|
||||||
|
('end_date', models.DateField()),
|
||||||
|
('start_time', models.TimeField()),
|
||||||
|
('end_time', models.TimeField()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='LocationGroupStop',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('location_group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pt_map.locationgroup')),
|
||||||
|
('stop', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pt_map.stop')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-06-02 17:10
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pt_map', '0008_area_farelegrule_faremedia_fareproduct_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='FareMedia',
|
||||||
|
new_name='FareMedium',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-06-02 17:19
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pt_map', '0009_rename_faremedia_faremedium'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='Location',
|
||||||
|
new_name='LocationGeojson',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-06-02 17:21
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pt_map', '0010_rename_location_locationgeojson'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='LocationGeojson',
|
||||||
|
new_name='LocationsGeojson',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,44 +1,47 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Agency(models.Model):
|
class Agency(models.Model):
|
||||||
agency_id = models.BigAutoField(primary_key=True)
|
agency_id = models.CharField(max_length=255, primary_key=True)
|
||||||
agency_name = models.CharField(max_length=250)
|
agency_name = models.CharField(max_length=255)
|
||||||
agency_url = models.URLField()
|
agency_url = models.URLField()
|
||||||
agency_phone = models.CharField(max_length=15)
|
agency_timezone = models.CharField(max_length=255)
|
||||||
agency_email = models.EmailField()
|
agency_lang = models.CharField(max_length=2, blank=True, null=True)
|
||||||
agency_fare_url = models.URLField()
|
agency_phone = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
agency_fare_url = models.URLField(blank=True, null=True)
|
||||||
|
agency_email = models.EmailField(blank=True, null=True)
|
||||||
|
|
||||||
class Stop(models.Model):
|
class Stop(models.Model):
|
||||||
stop_id = models.BigAutoField(primary_key=True)
|
stop_id = models.CharField(max_length=255, primary_key=True)
|
||||||
stop_code = models.CharField(max_length=50)
|
stop_code = models.CharField(max_length=50, blank=True, null=True)
|
||||||
stop_name = models.CharField(max_length=250)
|
stop_name = models.CharField(max_length=255)
|
||||||
tts_stop_name = models.CharField(max_length=250)
|
stop_desc = models.TextField(blank=True, null=True)
|
||||||
stop_desc = models.CharField(max_length=500)
|
stop_lat = models.FloatField()
|
||||||
stop_lat = models.IntegerField()
|
stop_lon = models.FloatField()
|
||||||
stop_lon = models.IntegerField()
|
zone_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
zone_id = models.IntegerField(unique=True)
|
|
||||||
stop_url = models.URLField(blank=True, null=True)
|
stop_url = models.URLField(blank=True, null=True)
|
||||||
location_type = models.IntegerField(blank=True, null=True)
|
location_type = models.IntegerField(blank=True, null=True)
|
||||||
parent_station = models.ForeignKey('self', on_delete=models.SET_NULL, blank=True, null=True)
|
parent_station = models.ForeignKey('self', on_delete=models.SET_NULL, blank=True, null=True)
|
||||||
stop_timezone = models.CharField(max_length=255, blank=True, null=True)
|
stop_timezone = models.CharField(max_length=255, blank=True, null=True)
|
||||||
wheelchair_boarding = models.IntegerField(blank=True, null=True)
|
wheelchair_boarding = models.IntegerField(blank=True, null=True)
|
||||||
level_id = models.CharField(max_length=255, blank=True, null=True)
|
level_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
platform_code = models.CharField(max_length=255, blank=True, null=True)
|
platform_code = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
class Route(models.Model):
|
class Route(models.Model):
|
||||||
route_id = models.BigAutoField(primary_key=True)
|
route_id = models.CharField(max_length=255, primary_key=True)
|
||||||
agency = models.ForeignKey(Agency, on_delete=models.CASCADE)
|
agency = models.ForeignKey(Agency, on_delete=models.CASCADE, blank=True, null=True)
|
||||||
route_short_name = models.CharField(max_length=255)
|
route_short_name = models.CharField(max_length=50)
|
||||||
route_long_name = models.CharField(max_length=255)
|
route_long_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
route_desc = models.TextField(blank=True, null=True)
|
route_desc = models.TextField(blank=True, null=True)
|
||||||
route_type = models.IntegerField()
|
route_type = models.IntegerField(default=0)
|
||||||
route_url = models.URLField(blank=True, null=True)
|
route_url = models.URLField(blank=True, null=True)
|
||||||
route_color = models.CharField(max_length=6, blank=True, null=True)
|
route_color = models.CharField(max_length=6, blank=True, null=True)
|
||||||
route_text_color = models.CharField(max_length=6, blank=True, null=True)
|
route_text_color = models.CharField(max_length=6, blank=True, null=True)
|
||||||
|
route_sort_order = models.IntegerField(blank=True, null=True)
|
||||||
|
continuous_pickup = models.IntegerField(blank=True, null=True)
|
||||||
|
continuous_drop_off = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
class Trip(models.Model):
|
class Trip(models.Model):
|
||||||
trip_id = models.BigAutoField(primary_key=True)
|
trip_id = models.CharField(max_length=255, primary_key=True)
|
||||||
route = models.ForeignKey(Route, on_delete=models.CASCADE)
|
route = models.ForeignKey(Route, on_delete=models.CASCADE)
|
||||||
service_id = models.CharField(max_length=255)
|
service_id = models.CharField(max_length=255)
|
||||||
trip_headsign = models.CharField(max_length=255, blank=True, null=True)
|
trip_headsign = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
@ -51,8 +54,8 @@ class Trip(models.Model):
|
||||||
|
|
||||||
class StopTime(models.Model):
|
class StopTime(models.Model):
|
||||||
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
|
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
|
||||||
arrival_time = models.TimeField()
|
arrival_time = models.TimeField(blank=True, null=True)
|
||||||
departure_time = models.TimeField()
|
departure_time = models.TimeField(blank=True, null=True)
|
||||||
stop = models.ForeignKey(Stop, on_delete=models.CASCADE)
|
stop = models.ForeignKey(Stop, on_delete=models.CASCADE)
|
||||||
stop_sequence = models.IntegerField()
|
stop_sequence = models.IntegerField()
|
||||||
stop_headsign = models.CharField(max_length=255, blank=True, null=True)
|
stop_headsign = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
@ -65,7 +68,7 @@ class StopTime(models.Model):
|
||||||
unique_together = (('trip', 'stop_sequence'),)
|
unique_together = (('trip', 'stop_sequence'),)
|
||||||
|
|
||||||
class Calendar(models.Model):
|
class Calendar(models.Model):
|
||||||
service_id = models.BigAutoField(primary_key=True)
|
service_id = models.CharField(max_length=255, primary_key=True)
|
||||||
monday = models.BooleanField()
|
monday = models.BooleanField()
|
||||||
tuesday = models.BooleanField()
|
tuesday = models.BooleanField()
|
||||||
wednesday = models.BooleanField()
|
wednesday = models.BooleanField()
|
||||||
|
|
@ -77,18 +80,17 @@ class Calendar(models.Model):
|
||||||
end_date = models.DateField()
|
end_date = models.DateField()
|
||||||
|
|
||||||
class CalendarDate(models.Model):
|
class CalendarDate(models.Model):
|
||||||
service_id = models.ForeignKey(Calendar, on_delete=models.CASCADE)
|
service_id = models.CharField(max_length=255)
|
||||||
date = models.DateField()
|
date = models.DateField()
|
||||||
exception_type = models.IntegerField()
|
exception_type = models.IntegerField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (('service_id', 'date'),)
|
unique_together = (('service_id', 'date'),)
|
||||||
|
|
||||||
|
|
||||||
class FareAttribute(models.Model):
|
class FareAttribute(models.Model):
|
||||||
fare_id = models.BigAutoField(primary_key=True)
|
fare_id = models.CharField(max_length=255, primary_key=True)
|
||||||
price = models.FloatField()
|
price = models.FloatField()
|
||||||
currency_type = models.CharField(max_length=255)
|
currency_type = models.CharField(max_length=3)
|
||||||
payment_method = models.IntegerField()
|
payment_method = models.IntegerField()
|
||||||
transfers = models.IntegerField()
|
transfers = models.IntegerField()
|
||||||
agency = models.ForeignKey(Agency, on_delete=models.CASCADE, blank=True, null=True)
|
agency = models.ForeignKey(Agency, on_delete=models.CASCADE, blank=True, null=True)
|
||||||
|
|
@ -123,15 +125,124 @@ class Transfer(models.Model):
|
||||||
to_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='transfers_to')
|
to_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='transfers_to')
|
||||||
transfer_type = models.IntegerField()
|
transfer_type = models.IntegerField()
|
||||||
min_transfer_time = models.IntegerField(blank=True, null=True)
|
min_transfer_time = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (('from_stop', 'to_stop'),)
|
unique_together = (('from_stop', 'to_stop'),)
|
||||||
|
|
||||||
|
class Pathway(models.Model):
|
||||||
|
pathway_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
from_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='pathways_from')
|
||||||
|
to_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='pathways_to')
|
||||||
|
pathway_mode = models.IntegerField()
|
||||||
|
is_bidirectional = models.IntegerField()
|
||||||
|
length = models.FloatField(blank=True, null=True)
|
||||||
|
traversal_time = models.IntegerField(blank=True, null=True)
|
||||||
|
stair_count = models.IntegerField(blank=True, null=True)
|
||||||
|
max_slope = models.FloatField(blank=True, null=True)
|
||||||
|
min_width = models.FloatField(blank=True, null=True)
|
||||||
|
signposted_as = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
reversed_signposted_as = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
class Level(models.Model):
|
||||||
|
level_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
level_index = models.FloatField()
|
||||||
|
level_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
class FeedInfo(models.Model):
|
class FeedInfo(models.Model):
|
||||||
feed_publisher_name = models.CharField(max_length=255)
|
feed_publisher_name = models.CharField(max_length=255)
|
||||||
feed_publisher_url = models.URLField()
|
feed_publisher_url = models.URLField()
|
||||||
feed_lang = models.CharField(max_length=255)
|
feed_lang = models.CharField(max_length=255)
|
||||||
|
default_lang = models.CharField(max_length=255, blank=True, null=True)
|
||||||
feed_start_date = models.DateField(blank=True, null=True)
|
feed_start_date = models.DateField(blank=True, null=True)
|
||||||
feed_end_date = models.DateField(blank=True, null=True)
|
feed_end_date = models.DateField(blank=True, null=True)
|
||||||
feed_version = models.CharField(max_length=255, blank=True, null=True)
|
feed_version = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
feed_contact_email = models.EmailField(blank=True, null=True)
|
||||||
|
feed_contact_url = models.URLField(blank=True, null=True)
|
||||||
feed_id = models.BigAutoField(primary_key=True)
|
feed_id = models.BigAutoField(primary_key=True)
|
||||||
|
|
||||||
|
class LocationsGeojson(models.Model):
|
||||||
|
location_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
location_name = models.CharField(max_length=255)
|
||||||
|
location_lat = models.FloatField()
|
||||||
|
location_lon = models.FloatField()
|
||||||
|
location_type = models.CharField(max_length=255)
|
||||||
|
parent_location_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
wheelchair_boarding = models.BooleanField(blank=True, null=True)
|
||||||
|
|
||||||
|
class BookingRule(models.Model):
|
||||||
|
booking_rule_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
|
||||||
|
start_time = models.TimeField(blank=True, null=True)
|
||||||
|
end_time = models.TimeField(blank=True, null=True)
|
||||||
|
booking_type = models.CharField(max_length=255)
|
||||||
|
rule_criteria = models.TextField(blank=True, null=True)
|
||||||
|
booking_rule_instructions = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Translation(models.Model):
|
||||||
|
table_name = models.CharField(max_length=255)
|
||||||
|
field_name = models.CharField(max_length=255)
|
||||||
|
language = models.CharField(max_length=2)
|
||||||
|
translation = models.TextField()
|
||||||
|
|
||||||
|
class Attribution(models.Model):
|
||||||
|
attribution_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
agency = models.ForeignKey(Agency, on_delete=models.CASCADE)
|
||||||
|
attribution_name = models.CharField(max_length=255)
|
||||||
|
attribution_url = models.URLField()
|
||||||
|
attribution_email = models.EmailField(blank=True, null=True)
|
||||||
|
attribution_phone = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
|
||||||
|
class LocationGroup(models.Model):
|
||||||
|
location_group_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
location_group_name = models.CharField(max_length=255)
|
||||||
|
location_group_type = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
class LocationGroupStop(models.Model):
|
||||||
|
location_group = models.ForeignKey(LocationGroup, on_delete=models.CASCADE)
|
||||||
|
stop = models.ForeignKey(Stop, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
class RouteNetwork(models.Model):
|
||||||
|
route_network_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
route_network_name = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
class Network(models.Model):
|
||||||
|
network_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
network_name = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
class StopArea(models.Model):
|
||||||
|
stop_area_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
stop_area_name = models.CharField(max_length=255)
|
||||||
|
stop_area_description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Area(models.Model):
|
||||||
|
area_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
area_name = models.CharField(max_length=255)
|
||||||
|
area_description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
class FareMedium(models.Model):
|
||||||
|
fare_media_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
fare_media_name = models.CharField(max_length=255)
|
||||||
|
fare_media_description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
class FareProduct(models.Model):
|
||||||
|
fare_product_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
fare_product_name = models.CharField(max_length=255)
|
||||||
|
fare_product_description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
class FareLegRule(models.Model):
|
||||||
|
fare_leg_rule_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
fare_leg_rule_name = models.CharField(max_length=255)
|
||||||
|
fare_leg_rule_description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
class FareTransferRule(models.Model):
|
||||||
|
fare_transfer_rule_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
fare_transfer_rule_name = models.CharField(max_length=255)
|
||||||
|
fare_transfer_rule_description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Timeframe(models.Model):
|
||||||
|
time_frame_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
start_date = models.DateField()
|
||||||
|
end_date = models.DateField()
|
||||||
|
start_time = models.TimeField()
|
||||||
|
end_time = models.TimeField()
|
||||||
|
|
||||||
|
|
|
||||||
137
transport_accessibility/pt_map/models.py.bck
Normal file
137
transport_accessibility/pt_map/models.py.bck
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Agency(models.Model):
|
||||||
|
agency_id = models.BigAutoField(primary_key=True)
|
||||||
|
agency_name = models.CharField(max_length=250)
|
||||||
|
agency_url = models.URLField()
|
||||||
|
agency_phone = models.CharField(max_length=15)
|
||||||
|
agency_email = models.EmailField()
|
||||||
|
agency_fare_url = models.URLField()
|
||||||
|
|
||||||
|
class Stop(models.Model):
|
||||||
|
stop_id = models.BigAutoField(primary_key=True)
|
||||||
|
stop_code = models.CharField(max_length=50)
|
||||||
|
stop_name = models.CharField(max_length=250)
|
||||||
|
tts_stop_name = models.CharField(max_length=250)
|
||||||
|
stop_desc = models.CharField(max_length=500)
|
||||||
|
stop_lat = models.IntegerField()
|
||||||
|
stop_lon = models.IntegerField()
|
||||||
|
zone_id = models.IntegerField(unique=True)
|
||||||
|
stop_url = models.URLField(blank=True, null=True)
|
||||||
|
location_type = models.IntegerField(blank=True, null=True)
|
||||||
|
parent_station = models.ForeignKey('self', on_delete=models.SET_NULL, blank=True, null=True)
|
||||||
|
stop_timezone = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
wheelchair_boarding = models.IntegerField(blank=True, null=True)
|
||||||
|
level_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
platform_code = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Route(models.Model):
|
||||||
|
route_id = models.BigAutoField(primary_key=True)
|
||||||
|
agency = models.ForeignKey(Agency, on_delete=models.CASCADE)
|
||||||
|
route_short_name = models.CharField(max_length=255)
|
||||||
|
route_long_name = models.CharField(max_length=255)
|
||||||
|
route_desc = models.TextField(blank=True, null=True)
|
||||||
|
route_type = models.IntegerField()
|
||||||
|
route_url = models.URLField(blank=True, null=True)
|
||||||
|
route_color = models.CharField(max_length=6, blank=True, null=True)
|
||||||
|
route_text_color = models.CharField(max_length=6, blank=True, null=True)
|
||||||
|
|
||||||
|
class Trip(models.Model):
|
||||||
|
trip_id = models.BigAutoField(primary_key=True)
|
||||||
|
route = models.ForeignKey(Route, on_delete=models.CASCADE)
|
||||||
|
service_id = models.CharField(max_length=255)
|
||||||
|
trip_headsign = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
trip_short_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
direction_id = models.IntegerField(blank=True, null=True)
|
||||||
|
block_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
shape_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
wheelchair_accessible = models.IntegerField(blank=True, null=True)
|
||||||
|
bikes_allowed = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class StopTime(models.Model):
|
||||||
|
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
|
||||||
|
arrival_time = models.TimeField()
|
||||||
|
departure_time = models.TimeField()
|
||||||
|
stop = models.ForeignKey(Stop, on_delete=models.CASCADE)
|
||||||
|
stop_sequence = models.IntegerField()
|
||||||
|
stop_headsign = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
pickup_type = models.IntegerField(blank=True, null=True)
|
||||||
|
drop_off_type = models.IntegerField(blank=True, null=True)
|
||||||
|
shape_dist_traveled = models.FloatField(blank=True, null=True)
|
||||||
|
timepoint = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (('trip', 'stop_sequence'),)
|
||||||
|
|
||||||
|
class Calendar(models.Model):
|
||||||
|
service_id = models.BigAutoField(primary_key=True)
|
||||||
|
monday = models.BooleanField()
|
||||||
|
tuesday = models.BooleanField()
|
||||||
|
wednesday = models.BooleanField()
|
||||||
|
thursday = models.BooleanField()
|
||||||
|
friday = models.BooleanField()
|
||||||
|
saturday = models.BooleanField()
|
||||||
|
sunday = models.BooleanField()
|
||||||
|
start_date = models.DateField()
|
||||||
|
end_date = models.DateField()
|
||||||
|
|
||||||
|
class CalendarDate(models.Model):
|
||||||
|
service_id = models.ForeignKey(Calendar, on_delete=models.CASCADE)
|
||||||
|
date = models.DateField()
|
||||||
|
exception_type = models.IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (('service_id', 'date'),)
|
||||||
|
|
||||||
|
|
||||||
|
class FareAttribute(models.Model):
|
||||||
|
fare_id = models.BigAutoField(primary_key=True)
|
||||||
|
price = models.FloatField()
|
||||||
|
currency_type = models.CharField(max_length=255)
|
||||||
|
payment_method = models.IntegerField()
|
||||||
|
transfers = models.IntegerField()
|
||||||
|
agency = models.ForeignKey(Agency, on_delete=models.CASCADE, blank=True, null=True)
|
||||||
|
transfer_duration = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class FareRule(models.Model):
|
||||||
|
fare = models.ForeignKey(FareAttribute, on_delete=models.CASCADE)
|
||||||
|
route = models.ForeignKey(Route, on_delete=models.CASCADE, blank=True, null=True)
|
||||||
|
origin_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
destination_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
contains_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
class Shape(models.Model):
|
||||||
|
shape_id = models.CharField(max_length=255)
|
||||||
|
shape_pt_lat = models.FloatField()
|
||||||
|
shape_pt_lon = models.FloatField()
|
||||||
|
shape_pt_sequence = models.IntegerField()
|
||||||
|
shape_dist_traveled = models.FloatField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (('shape_id', 'shape_pt_sequence'),)
|
||||||
|
|
||||||
|
class Frequency(models.Model):
|
||||||
|
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
|
||||||
|
start_time = models.TimeField()
|
||||||
|
end_time = models.TimeField()
|
||||||
|
headway_secs = models.IntegerField()
|
||||||
|
exact_times = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Transfer(models.Model):
|
||||||
|
from_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='transfers_from')
|
||||||
|
to_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='transfers_to')
|
||||||
|
transfer_type = models.IntegerField()
|
||||||
|
min_transfer_time = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (('from_stop', 'to_stop'),)
|
||||||
|
|
||||||
|
class FeedInfo(models.Model):
|
||||||
|
feed_publisher_name = models.CharField(max_length=255)
|
||||||
|
feed_publisher_url = models.URLField()
|
||||||
|
feed_lang = models.CharField(max_length=255)
|
||||||
|
feed_start_date = models.DateField(blank=True, null=True)
|
||||||
|
feed_end_date = models.DateField(blank=True, null=True)
|
||||||
|
feed_version = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
feed_id = models.BigAutoField(primary_key=True)
|
||||||
162
transport_accessibility/pt_map/models.py.bck2
Normal file
162
transport_accessibility/pt_map/models.py.bck2
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Agency(models.Model):
|
||||||
|
agency_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
agency_name = models.CharField(max_length=255)
|
||||||
|
agency_url = models.URLField()
|
||||||
|
agency_timezone = models.CharField(max_length=255)
|
||||||
|
agency_lang = models.CharField(max_length=2, blank=True, null=True)
|
||||||
|
agency_phone = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
agency_fare_url = models.URLField(blank=True, null=True)
|
||||||
|
agency_email = models.EmailField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Stop(models.Model):
|
||||||
|
stop_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
stop_code = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
stop_name = models.CharField(max_length=255)
|
||||||
|
stop_desc = models.TextField(blank=True, null=True)
|
||||||
|
stop_lat = models.FloatField()
|
||||||
|
stop_lon = models.FloatField()
|
||||||
|
zone_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
stop_url = models.URLField(blank=True, null=True)
|
||||||
|
location_type = models.IntegerField(blank=True, null=True)
|
||||||
|
parent_station = models.ForeignKey('self', on_delete=models.SET_NULL, blank=True, null=True)
|
||||||
|
stop_timezone = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
wheelchair_boarding = models.IntegerField(blank=True, null=True)
|
||||||
|
level_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
platform_code = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
|
||||||
|
class Route(models.Model):
|
||||||
|
route_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
agency = models.ForeignKey(Agency, on_delete=models.CASCADE, blank=True, null=True)
|
||||||
|
route_short_name = models.CharField(max_length=50)
|
||||||
|
route_long_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
route_desc = models.TextField(blank=True, null=True)
|
||||||
|
route_type = models.IntegerField(default=0)
|
||||||
|
route_url = models.URLField(blank=True, null=True)
|
||||||
|
route_color = models.CharField(max_length=6, blank=True, null=True)
|
||||||
|
route_text_color = models.CharField(max_length=6, blank=True, null=True)
|
||||||
|
route_sort_order = models.IntegerField(blank=True, null=True)
|
||||||
|
continuous_pickup = models.IntegerField(blank=True, null=True)
|
||||||
|
continuous_drop_off = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Trip(models.Model):
|
||||||
|
trip_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
route = models.ForeignKey(Route, on_delete=models.CASCADE)
|
||||||
|
service_id = models.CharField(max_length=255)
|
||||||
|
trip_headsign = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
trip_short_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
direction_id = models.IntegerField(blank=True, null=True)
|
||||||
|
block_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
shape_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
wheelchair_accessible = models.IntegerField(blank=True, null=True)
|
||||||
|
bikes_allowed = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class StopTime(models.Model):
|
||||||
|
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
|
||||||
|
arrival_time = models.TimeField(blank=True, null=True)
|
||||||
|
departure_time = models.TimeField(blank=True, null=True)
|
||||||
|
stop = models.ForeignKey(Stop, on_delete=models.CASCADE)
|
||||||
|
stop_sequence = models.IntegerField()
|
||||||
|
stop_headsign = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
pickup_type = models.IntegerField(blank=True, null=True)
|
||||||
|
drop_off_type = models.IntegerField(blank=True, null=True)
|
||||||
|
shape_dist_traveled = models.FloatField(blank=True, null=True)
|
||||||
|
timepoint = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (('trip', 'stop_sequence'),)
|
||||||
|
|
||||||
|
class Calendar(models.Model):
|
||||||
|
service_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
monday = models.BooleanField()
|
||||||
|
tuesday = models.BooleanField()
|
||||||
|
wednesday = models.BooleanField()
|
||||||
|
thursday = models.BooleanField()
|
||||||
|
friday = models.BooleanField()
|
||||||
|
saturday = models.BooleanField()
|
||||||
|
sunday = models.BooleanField()
|
||||||
|
start_date = models.DateField()
|
||||||
|
end_date = models.DateField()
|
||||||
|
|
||||||
|
class CalendarDate(models.Model):
|
||||||
|
service_id = models.CharField(max_length=255)
|
||||||
|
date = models.DateField()
|
||||||
|
exception_type = models.IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (('service_id', 'date'),)
|
||||||
|
|
||||||
|
class FareAttribute(models.Model):
|
||||||
|
fare_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
price = models.FloatField()
|
||||||
|
currency_type = models.CharField(max_length=3)
|
||||||
|
payment_method = models.IntegerField()
|
||||||
|
transfers = models.IntegerField()
|
||||||
|
agency = models.ForeignKey(Agency, on_delete=models.CASCADE, blank=True, null=True)
|
||||||
|
transfer_duration = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class FareRule(models.Model):
|
||||||
|
fare = models.ForeignKey(FareAttribute, on_delete=models.CASCADE)
|
||||||
|
route = models.ForeignKey(Route, on_delete=models.CASCADE, blank=True, null=True)
|
||||||
|
origin_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
destination_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
contains_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
class Shape(models.Model):
|
||||||
|
shape_id = models.CharField(max_length=255)
|
||||||
|
shape_pt_lat = models.FloatField()
|
||||||
|
shape_pt_lon = models.FloatField()
|
||||||
|
shape_pt_sequence = models.IntegerField()
|
||||||
|
shape_dist_traveled = models.FloatField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (('shape_id', 'shape_pt_sequence'),)
|
||||||
|
|
||||||
|
class Frequency(models.Model):
|
||||||
|
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
|
||||||
|
start_time = models.TimeField()
|
||||||
|
end_time = models.TimeField()
|
||||||
|
headway_secs = models.IntegerField()
|
||||||
|
exact_times = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Transfer(models.Model):
|
||||||
|
from_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='transfers_from')
|
||||||
|
to_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='transfers_to')
|
||||||
|
transfer_type = models.IntegerField()
|
||||||
|
min_transfer_time = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (('from_stop', 'to_stop'),)
|
||||||
|
|
||||||
|
class Pathway(models.Model):
|
||||||
|
pathway_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
from_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='pathways_from')
|
||||||
|
to_stop = models.ForeignKey(Stop, on_delete=models.CASCADE, related_name='pathways_to')
|
||||||
|
pathway_mode = models.IntegerField()
|
||||||
|
is_bidirectional = models.IntegerField()
|
||||||
|
length = models.FloatField(blank=True, null=True)
|
||||||
|
traversal_time = models.IntegerField(blank=True, null=True)
|
||||||
|
stair_count = models.IntegerField(blank=True, null=True)
|
||||||
|
max_slope = models.FloatField(blank=True, null=True)
|
||||||
|
min_width = models.FloatField(blank=True, null=True)
|
||||||
|
signposted_as = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
reversed_signposted_as = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
class Level(models.Model):
|
||||||
|
level_id = models.CharField(max_length=255, primary_key=True)
|
||||||
|
level_index = models.FloatField()
|
||||||
|
level_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
class FeedInfo(models.Model):
|
||||||
|
feed_publisher_name = models.CharField(max_length=255)
|
||||||
|
feed_publisher_url = models.URLField()
|
||||||
|
feed_lang = models.CharField(max_length=255)
|
||||||
|
default_lang = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
feed_start_date = models.DateField(blank=True, null=True)
|
||||||
|
feed_end_date = models.DateField(blank=True, null=True)
|
||||||
|
feed_version = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
feed_contact_email = models.EmailField(blank=True, null=True)
|
||||||
|
feed_contact_url = models.URLField(blank=True, null=True)
|
||||||
|
feed_id = models.BigAutoField(primary_key=True)
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user