added geopy
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
__pycache__/
|
||||||
|
__pycache__/*
|
||||||
|
besser_tanken/__pycache__
|
||||||
|
merged.feather
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "tankerkoenig-data"]
|
||||||
|
path = tankerkoenig-data
|
||||||
|
url = https://tankerkoenig@dev.azure.com/tankerkoenig/tankerkoenig-data/_git/tankerkoenig-data
|
||||||
Generated
+3
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Generated
+16
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CsvFileAttributes">
|
||||||
|
<option name="attributeMap">
|
||||||
|
<map>
|
||||||
|
<entry key="/tankerkoenig-data/stations/2021/02/2021-02-07-stations.csv">
|
||||||
|
<value>
|
||||||
|
<Attribute>
|
||||||
|
<option name="separator" value="," />
|
||||||
|
</Attribute>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<value>
|
||||||
|
<list size="42">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="cffi" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="numpy" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="requests" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="markupsafe" />
|
||||||
|
<item index="4" class="java.lang.String" itemvalue="pyrsistent" />
|
||||||
|
<item index="5" class="java.lang.String" itemvalue="certifi" />
|
||||||
|
<item index="6" class="java.lang.String" itemvalue="lxml" />
|
||||||
|
<item index="7" class="java.lang.String" itemvalue="urllib3" />
|
||||||
|
<item index="8" class="java.lang.String" itemvalue="itsdangerous" />
|
||||||
|
<item index="9" class="java.lang.String" itemvalue="jsonschema" />
|
||||||
|
<item index="10" class="java.lang.String" itemvalue="pynacl" />
|
||||||
|
<item index="11" class="java.lang.String" itemvalue="flask-restx" />
|
||||||
|
<item index="12" class="java.lang.String" itemvalue="werkzeug" />
|
||||||
|
<item index="13" class="java.lang.String" itemvalue="six" />
|
||||||
|
<item index="14" class="java.lang.String" itemvalue="aniso8601" />
|
||||||
|
<item index="15" class="java.lang.String" itemvalue="flask-jwt-extended" />
|
||||||
|
<item index="16" class="java.lang.String" itemvalue="cryptography" />
|
||||||
|
<item index="17" class="java.lang.String" itemvalue="click" />
|
||||||
|
<item index="18" class="java.lang.String" itemvalue="attrs" />
|
||||||
|
<item index="19" class="java.lang.String" itemvalue="bcrypt" />
|
||||||
|
<item index="20" class="java.lang.String" itemvalue="jinja2" />
|
||||||
|
<item index="21" class="java.lang.String" itemvalue="pandas" />
|
||||||
|
<item index="22" class="java.lang.String" itemvalue="paramiko" />
|
||||||
|
<item index="23" class="java.lang.String" itemvalue="user-agents" />
|
||||||
|
<item index="24" class="java.lang.String" itemvalue="flask" />
|
||||||
|
<item index="25" class="java.lang.String" itemvalue="pyjwt" />
|
||||||
|
<item index="26" class="java.lang.String" itemvalue="idna" />
|
||||||
|
<item index="27" class="java.lang.String" itemvalue="blinker" />
|
||||||
|
<item index="28" class="java.lang.String" itemvalue="flask-cors" />
|
||||||
|
<item index="29" class="java.lang.String" itemvalue="flask-httpauth" />
|
||||||
|
<item index="30" class="java.lang.String" itemvalue="flask-sitemap" />
|
||||||
|
<item index="31" class="java.lang.String" itemvalue="mariadb" />
|
||||||
|
<item index="32" class="java.lang.String" itemvalue="tqdm" />
|
||||||
|
<item index="33" class="java.lang.String" itemvalue="protobuf" />
|
||||||
|
<item index="34" class="java.lang.String" itemvalue="tabulate" />
|
||||||
|
<item index="35" class="java.lang.String" itemvalue="pytimeparse" />
|
||||||
|
<item index="36" class="java.lang.String" itemvalue="sqlite3-to-mysql" />
|
||||||
|
<item index="37" class="java.lang.String" itemvalue="packaging" />
|
||||||
|
<item index="38" class="java.lang.String" itemvalue="mysql-connector-python" />
|
||||||
|
<item index="39" class="java.lang.String" itemvalue="sqlalchemy" />
|
||||||
|
<item index="40" class="java.lang.String" itemvalue="simplejson" />
|
||||||
|
<item index="41" class="java.lang.String" itemvalue="unittest" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredErrors">
|
||||||
|
<list>
|
||||||
|
<option value="E402" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
Generated
+4
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Poetry (besser_tanken)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/besser_tanken.iml" filepath="$PROJECT_DIR$/.idea/besser_tanken.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/tankerkoenig-data" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
conf_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
stations_dir = os.path.abspath(
|
||||||
|
os.path.join(conf_dir, os.pardir, "tankerkoenig-data", "stations"))
|
||||||
|
prices_dir = os.path.abspath(
|
||||||
|
os.path.join(conf_dir, os.pardir, "tankerkoenig-data", "prices"))
|
||||||
|
feather_dir = os.path.abspath(
|
||||||
|
os.path.join(conf_dir, os.pardir, "feather_data"))
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import concurrent
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
from datetime import date, timedelta
|
||||||
|
from glob import glob
|
||||||
|
from threading import Thread, Lock
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
from besser_tanken.config import stations_dir, prices_dir
|
||||||
|
from besser_tanken.data_tools import fix_date_time_strings
|
||||||
|
|
||||||
|
files_re = re.compile(r"(\d\d\d\d)-(\d\d)-(\d\d)-(\S*).csv")
|
||||||
|
|
||||||
|
prices_cache = dict()
|
||||||
|
stations_cache = dict()
|
||||||
|
|
||||||
|
|
||||||
|
def load_date_hierarchical_file(base_dir, year=None, month=None,
|
||||||
|
day=None) -> str:
|
||||||
|
year_dirs = glob(f"{base_dir}/*/")
|
||||||
|
years = []
|
||||||
|
for y_d in year_dirs:
|
||||||
|
y = y_d[:-1].rsplit('/', maxsplit=1)[1]
|
||||||
|
years.append(int(y))
|
||||||
|
if year is None:
|
||||||
|
year = sorted(years)[-1]
|
||||||
|
if int(year) not in years:
|
||||||
|
raise ValueError(f"No file for year {year}!")
|
||||||
|
year_dir = f"{base_dir}/{year}"
|
||||||
|
|
||||||
|
month_dirs = glob(f"{year_dir}/*/")
|
||||||
|
months = []
|
||||||
|
for m_d in month_dirs:
|
||||||
|
m = m_d[:-1].rsplit('/', maxsplit=1)[1]
|
||||||
|
months.append(int(m))
|
||||||
|
if month is None:
|
||||||
|
month = sorted(months)[-1]
|
||||||
|
if int(month) not in months:
|
||||||
|
raise ValueError(f"No file found for month {month}!")
|
||||||
|
month_dir = f"{year_dir}/{month:02d}"
|
||||||
|
|
||||||
|
day_files = glob(f"{month_dir}/*.csv")
|
||||||
|
days = []
|
||||||
|
file_type = None
|
||||||
|
for d in day_files:
|
||||||
|
match = files_re.search(d)
|
||||||
|
if match:
|
||||||
|
days.append(int(match.group(3)))
|
||||||
|
file_type = match.group(4)
|
||||||
|
if day is None:
|
||||||
|
day = sorted(days)[-1]
|
||||||
|
if int(day) not in days or file_type is None:
|
||||||
|
raise ValueError(f"No file found for day {day}!")
|
||||||
|
|
||||||
|
return os.path.join(month_dir,
|
||||||
|
f"{year}-{month:02d}-{day:02d}-{file_type}.csv")
|
||||||
|
|
||||||
|
|
||||||
|
def load_prices(year=None, month=None, day=None) -> list:
|
||||||
|
file = load_date_hierarchical_file(prices_dir, year, month, day)
|
||||||
|
if file in prices_cache:
|
||||||
|
logger.info(f"returning prices from cache for file: {file}")
|
||||||
|
return prices_cache[file]
|
||||||
|
logger.info(f"loading prices file: {file}")
|
||||||
|
with open(file, newline='') as csv_file:
|
||||||
|
res = list(csv.DictReader(csv_file))
|
||||||
|
logger.info(f"got {len(res)} prices")
|
||||||
|
res = fix_date_time_strings(res)
|
||||||
|
prices_cache[file] = res
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def load_prices_from(year=None, month=None, day=None, threading=True) -> list:
|
||||||
|
today = date.today()
|
||||||
|
start_date = date(year, month, day)
|
||||||
|
res = []
|
||||||
|
|
||||||
|
def _threaded_loading(y: int, m: int, d: int):
|
||||||
|
try:
|
||||||
|
return load_prices(y, m, d)
|
||||||
|
except ValueError:
|
||||||
|
return None # ignore ValueError, just do nothing
|
||||||
|
|
||||||
|
if threading:
|
||||||
|
with ThreadPoolExecutor(max_workers=16) as executor:
|
||||||
|
futures = []
|
||||||
|
while start_date < today:
|
||||||
|
# Thread(target=_threaded_loading, args=(start_date.year,
|
||||||
|
# start_date.month,
|
||||||
|
# start_date.day,)).start()
|
||||||
|
futures.append(
|
||||||
|
executor.submit(_threaded_loading, y=start_date.year,
|
||||||
|
m=start_date.month, d=start_date.day))
|
||||||
|
start_date = start_date + timedelta(days=1)
|
||||||
|
|
||||||
|
for future in as_completed(futures):
|
||||||
|
try:
|
||||||
|
r = future.result()
|
||||||
|
if r is not None:
|
||||||
|
res.extend(r)
|
||||||
|
except TimeoutError:
|
||||||
|
print("timeout occured")
|
||||||
|
else:
|
||||||
|
while start_date < today:
|
||||||
|
try:
|
||||||
|
res.extend(load_prices(start_date.year,
|
||||||
|
start_date.month,
|
||||||
|
start_date.day))
|
||||||
|
|
||||||
|
start_date = start_date + timedelta(days=1)
|
||||||
|
except ValueError:
|
||||||
|
return res
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def load_stations(year=None, month=None, day=None) -> list:
|
||||||
|
file = load_date_hierarchical_file(stations_dir, year, month, day)
|
||||||
|
if file in stations_cache:
|
||||||
|
logger.info(f"returning stations from cache for file: {file}")
|
||||||
|
return stations_cache[file]
|
||||||
|
with open(file, newline='') as csv_file:
|
||||||
|
res = list(csv.DictReader(csv_file))
|
||||||
|
logger.info(f"got {len(res)} stations")
|
||||||
|
stations_cache[file] = res
|
||||||
|
return res
|
||||||
|
|
||||||
|
# stations_dir
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
import feather
|
||||||
|
import pandas
|
||||||
|
|
||||||
|
from besser_tanken.config import stations_dir
|
||||||
|
|
||||||
|
|
||||||
|
def group_prices(prices):
|
||||||
|
grouped_prices = defaultdict(list)
|
||||||
|
for p in prices:
|
||||||
|
grouped_prices[p['station_uuid']].append(p)
|
||||||
|
return grouped_prices
|
||||||
|
|
||||||
|
|
||||||
|
def fix_date_time_strings(price_dict_list):
|
||||||
|
for p in price_dict_list:
|
||||||
|
try:
|
||||||
|
date_time_string, tz_string = p['date'].rsplit('+', maxsplit=1)
|
||||||
|
if ':' in tz_string:
|
||||||
|
tz_string.replace(':', '')
|
||||||
|
else:
|
||||||
|
tz_string_len = len(tz_string)
|
||||||
|
if tz_string_len < 4:
|
||||||
|
tz_string += '0' * (4 - tz_string_len)
|
||||||
|
p['date'] = date_time_string + "+" + tz_string
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return price_dict_list
|
||||||
|
|
||||||
|
|
||||||
|
def convert_csv_to_feather(csv_file: str, feather_file: str = None):
|
||||||
|
path = 'my_data.feather'
|
||||||
|
df = pandas.read_csv(csv_file)
|
||||||
|
if feather_file is None:
|
||||||
|
feather_file = csv_file.rsplit('.', maxsplit=1)[0] + '.feather'
|
||||||
|
feather.write_dataframe(df, feather_file)
|
||||||
|
# df = feather.read_dataframe(path)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
convert_csv_to_feather(f"{stations_dir}/2021/01/2021-01-01-stations.csv")
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import date, timedelta, datetime
|
||||||
|
from glob import glob
|
||||||
|
from pprint import pprint
|
||||||
|
from typing import Union
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from besser_tanken.data_loader import load_stations, load_prices_from, \
|
||||||
|
load_prices
|
||||||
|
from besser_tanken.data_tools import group_prices
|
||||||
|
|
||||||
|
|
||||||
|
def _test_get_buehl_stations():
|
||||||
|
for s in load_stations():
|
||||||
|
if s['post_code'] in ['77815']:
|
||||||
|
yield s
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
buehl_station = next(_test_get_buehl_stations())
|
||||||
|
print(buehl_station)
|
||||||
|
for p in load_prices():
|
||||||
|
# for p in load_prices_from(2021, 4, 1):
|
||||||
|
print(p['date'])
|
||||||
|
print(datetime.strptime(p['date'],
|
||||||
|
'%Y-%m-%d %H:%M:%S%z'))
|
||||||
|
# exit()
|
||||||
|
|
||||||
|
print(p)
|
||||||
|
print(p['date'])
|
||||||
|
# if ":" == p['date'][-3]:
|
||||||
|
# p['date'] = p['date'][:-3] + p['date'][-2:]
|
||||||
|
# print(p['date'])
|
||||||
|
datetime_object = datetime.strptime(p['date'] + "00",
|
||||||
|
'%Y-%m-%d %H:%M:%S%z')
|
||||||
|
print(datetime_object)
|
||||||
|
exit()
|
||||||
|
prices = load_prices_from(2021, 4, 1)
|
||||||
|
# print(prices)
|
||||||
|
print(len(prices))
|
||||||
|
prices = group_prices(prices)
|
||||||
|
pprint(prices[buehl_station['uuid']])
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import pygeodb
|
||||||
|
print(pygeodb.distance("42897", "50933")) # strings
|
||||||
|
|
||||||
|
print(pygeodb.distance("77815", "78247")) # strings
|
||||||
|
|
||||||
|
# https://pypi.org/project/pyGeoDb/
|
||||||
File diff suppressed because one or more lines are too long
Generated
+2214
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "besser_tanken"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = ""
|
||||||
|
authors = ["Tobias Kurze <it@t-kurze.de>"]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.9"
|
||||||
|
loguru = "^0.5.3"
|
||||||
|
seaborn = "^0.11.1"
|
||||||
|
earthpy = "^0.9.2"
|
||||||
|
jupyter = "^1.0.0"
|
||||||
|
jupyter_nbextensions_configurator = "^0.4.1"
|
||||||
|
feather-format = "^0.4.1"
|
||||||
|
pygeodb = {git = "https://github.com/tkurze/pyGeoDb.git"}
|
||||||
|
|
||||||
|
[tool.poetry.dev-dependencies]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
File diff suppressed because one or more lines are too long
Submodule
+1
Submodule tankerkoenig-data added at afed4bbe40
Reference in New Issue
Block a user