diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | config.example.json | 39 | ||||
-rw-r--r-- | distances.py | 114 | ||||
-rw-r--r-- | shell.nix | 7 |
5 files changed, 170 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f70185 --- /dev/null +++ b/.gitignore | |||
@@ -0,0 +1,5 @@ | |||
1 | /config.json | ||
2 | /outputs/ | ||
3 | *.dat | ||
4 | *.gnuplot | ||
5 | *.json | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..71ec2f9 --- /dev/null +++ b/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | NIX_PATH=nixpkgs=https://releases.nixos.org/nixos/19.09/nixos-19.09.840.8bf142e001b/nixexprs.tar.xz | ||
2 | .PHONY: build | ||
3 | |||
4 | build: | ||
5 | NIX_PATH=$(NIX_PATH) nix-shell --pure --run "python distances.py" | ||
diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..fec6b23 --- /dev/null +++ b/config.example.json | |||
@@ -0,0 +1,39 @@ | |||
1 | { | ||
2 | "api_key": "your-api-key see https://developers.google.com/maps/documentation/distance-matrix/start", | ||
3 | "locations": { | ||
4 | "home": "6 Rue Ribotti, 06300 Nice, France", | ||
5 | "work": "43.586337,6.939781" | ||
6 | }, | ||
7 | "plots": { | ||
8 | "home-work": { | ||
9 | "days": ["monday","tuesday","wednesday","thursday","friday"], | ||
10 | "name": "Home -> Work", | ||
11 | "times": [ | ||
12 | "6:30", "7:00", "7:30", "8:00", "8:30", | ||
13 | "9:00", "9:30", "10:00" | ||
14 | ], | ||
15 | "origin": "home", | ||
16 | "destination": "work" | ||
17 | }, | ||
18 | "work-home": { | ||
19 | "days": ["monday","tuesday","wednesday","thursday","friday"], | ||
20 | "name": "Work -> Home", | ||
21 | "times": [ | ||
22 | "16:00", "16:30", "17:00", "17:30", "18:00", | ||
23 | "18:30", "19:00", "19:30", "20:00" | ||
24 | ], | ||
25 | "origin": "work", | ||
26 | "destination": "home" | ||
27 | }, | ||
28 | "work-gym": { | ||
29 | "days": ["monday"], | ||
30 | "name": "Work -> Monday’s gym", | ||
31 | "times": [ | ||
32 | "11:00", "11:15", "11:30", "11:45", | ||
33 | "12:00", "12:15" | ||
34 | ], | ||
35 | "origin": "work", | ||
36 | "destination": "330 Rue Henri Laugier, 06600 Antibes, France" | ||
37 | } | ||
38 | } | ||
39 | } | ||
diff --git a/distances.py b/distances.py new file mode 100644 index 0000000..e28caf7 --- /dev/null +++ b/distances.py | |||
@@ -0,0 +1,114 @@ | |||
1 | #!/usr/bin/env python | ||
2 | import requests | ||
3 | from datetime import datetime, timedelta | ||
4 | from collections import defaultdict | ||
5 | import json | ||
6 | import os, sys | ||
7 | import pytz | ||
8 | import argparse | ||
9 | |||
10 | URL= "https://maps.googleapis.com/maps/api/distancematrix/json" | ||
11 | |||
12 | parser = argparse.ArgumentParser(description='Plot traffic information for various origin-destinations') | ||
13 | parser.add_argument('-c', '--config', type=str, default='config.json', help='Config file to load (default config.jsonà') | ||
14 | parser.add_argument('-n', '--dry-run', action='store_true', help='Run in dry-run mode (don’t make any api call)') | ||
15 | parser.add_argument('-d', '--date', type=str, default='now', help='date (%%Y-%%m-%%d) for which to generate the plots') | ||
16 | parser.add_argument('-s', '--stats', action='store_true', help='only print number of weekly calls for the config') | ||
17 | args = parser.parse_args() | ||
18 | |||
19 | WEEKDAYS = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] | ||
20 | SETTINGS = json.load(open(args.config, "r")) | ||
21 | |||
22 | if args.date == "now": | ||
23 | date = datetime.now(tz=pytz.timezone("Europe/Paris")) | ||
24 | else: | ||
25 | date = datetime.strptime(args.date, "%Y-%m-%d").replace(tzinfo=pytz.timezone("Europe/Paris")) | ||
26 | |||
27 | def parse_plot(info): | ||
28 | def condition(d): | ||
29 | return "days" not in info or WEEKDAYS[d.weekday()] in info["days"] | ||
30 | return { | ||
31 | "name": info["name"], | ||
32 | "origin": SETTINGS.get("locations", {}).get(info["origin"], info["origin"]), | ||
33 | "destination": SETTINGS.get("locations", {}).get(info["destination"], info["destination"]), | ||
34 | "condition": condition, | ||
35 | "times": [t.split(":") for t in info["times"]], | ||
36 | } | ||
37 | |||
38 | plots = {} | ||
39 | for name,info in SETTINGS["plots"].items(): | ||
40 | plots[name] = parse_plot(info) | ||
41 | |||
42 | def to_time(date, hour, minute): | ||
43 | return date.replace(date.year, date.month, date.day, int(hour), int(minute), 0, 0) | ||
44 | |||
45 | def get(time, origin, destination): | ||
46 | keys = { | ||
47 | "origins": origin, | ||
48 | "destinations": destination, | ||
49 | "departure_time": int(time.timestamp()), | ||
50 | "key": SETTINGS["api_key"], | ||
51 | } | ||
52 | return requests.get(URL, params=keys) | ||
53 | |||
54 | def stats(): | ||
55 | total_calls_per_week = 0 | ||
56 | for k, infos in plots.items(): | ||
57 | calls_per_week = 0 | ||
58 | for i in range(0, 7): | ||
59 | if infos["condition"](date + timedelta(i)): | ||
60 | calls_per_week += 1 | ||
61 | total_calls_per_week += calls_per_week * len(infos["times"]) | ||
62 | print("{} calls per week to api".format(total_calls_per_week)) | ||
63 | if args.stats: | ||
64 | stats() | ||
65 | sys.exit(0) | ||
66 | |||
67 | gnuplot = """ | ||
68 | reset | ||
69 | set terminal png size 1024,768 enhanced font "Helvetica" | ||
70 | set output 'outputs/{fname}.png' | ||
71 | set datafile separator "\\t" | ||
72 | set xlabel "Time" | ||
73 | set ylabel "Duration" | ||
74 | |||
75 | set xdata time | ||
76 | set timefmt "%H:%M" | ||
77 | set format x "%H:%M" | ||
78 | set title "{date} {name}" | ||
79 | plot '{fname}.dat' using 1:2 with linespoints pointtype 5 title "" | ||
80 | """ | ||
81 | |||
82 | def get_times(name, infos, date, dry_run=False): | ||
83 | result = {} | ||
84 | if "condition" in infos and not infos["condition"](date): | ||
85 | return result | ||
86 | for time in infos["times"]: | ||
87 | t = to_time(date, time[0], time[1]) | ||
88 | try: | ||
89 | if dry_run: | ||
90 | v = 42 | ||
91 | else: | ||
92 | v = get(t, infos["origin"], infos["destination"]).json() | ||
93 | v = int(v["rows"][0]["elements"][0]["duration_in_traffic"]["value"] / 60) | ||
94 | except: | ||
95 | continue | ||
96 | result[t.strftime("%H:%M")] = v | ||
97 | with open(date.strftime("%Y-%m-%d_{}.json".format(name)), "w") as f: | ||
98 | f.write(json.dumps(result, indent=True)) | ||
99 | |||
100 | fname = date.strftime("%Y-%m-%d_{}".format(name)) | ||
101 | with open("{}.dat".format(fname), "w") as f: | ||
102 | for time, duration in result.items(): | ||
103 | f.write('{}\t{}\n'.format(time, duration)) | ||
104 | with open("{}.gnuplot".format(fname), "w") as f: | ||
105 | f.write(gnuplot.format(**{ | ||
106 | "fname": fname, | ||
107 | "name": infos["name"], | ||
108 | "date": date.strftime("%Y-%m-%d") | ||
109 | })) | ||
110 | os.system("gnuplot -c {}.gnuplot".format(fname)) | ||
111 | return result | ||
112 | |||
113 | for name, infos in plots.items(): | ||
114 | get_times(name, infos, date, dry_run=args.dry_run) | ||
diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..cdf128c --- /dev/null +++ b/shell.nix | |||
@@ -0,0 +1,7 @@ | |||
1 | { pkgs ? import <nixpkgs> {} }: | ||
2 | pkgs.mkShell { | ||
3 | buildInputs = [ | ||
4 | pkgs.gnuplot | ||
5 | (pkgs.python3.withPackages (p: [p.requests p.pytz])) | ||
6 | ]; | ||
7 | } | ||