Skip to content

Commit ed5afae

Browse files
authored
Merge pull request #362 from openego/feature/copy-from-oedb-to-local-db
Feature/copy from oedb to local db
2 parents f3d9844 + 8c2cccd commit ed5afae

File tree

2 files changed

+172
-2
lines changed

2 files changed

+172
-2
lines changed

preprocessing/utility/oedbtodb.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
4+
5+
Sources
6+
-------
7+
https://github.com/OpenEnergyPlatform/oedialect/blob/master/doc/example/oedialec
8+
https://stackoverflow.com/questions/21770829/sqlalchemy-copy-schema-and-data-of-subquery-to-another-database
9+
https://geoalchemy-2.readthedocs.io/en/latest/elements.html
10+
11+
"""
12+
13+
import oedialect
14+
15+
import sqlalchemy as sa
16+
17+
from egoio.tools import Base
18+
from importlib import import_module
19+
from sqlalchemy import event
20+
from sqlalchemy.orm import sessionmaker
21+
from sqlalchemy.orm.session import make_transient
22+
from geoalchemy2.elements import WKBElement
23+
24+
25+
def meta_url(schema, table):
26+
27+
base = "https://openenergy-platform.org/api/v0/"
28+
return base + "schema/" + schema + "/tables/" + table + "/meta/"
29+
30+
31+
def create_session(url):
32+
""" Returns SQLAlchemy session object
33+
34+
Parameters
35+
----------
36+
url : string
37+
Database dialect and connection arguments
38+
"""
39+
engine = sa.create_engine(url)
40+
return sessionmaker(bind=engine)()
41+
42+
43+
def unique_schema(qualified):
44+
""" Returns unique schema names
45+
46+
Parameters
47+
----------
48+
qualified : list
49+
List of qualified names in `schema.table` format
50+
"""
51+
52+
schemas = list(set([schema for schema, table in [i.split(".") for i in qualified]]))
53+
54+
return schemas
55+
56+
57+
def map_name_table(names, base):
58+
""" Create map from `schema.table` to associated table object
59+
60+
Parameters
61+
----------
62+
names : list
63+
List of qualified names in `schema.table` format
64+
base : `sqlalchemy.ext.declarative.api.Base`
65+
66+
"""
67+
mapper = {}
68+
69+
for name, obj in base.metadata.tables.items():
70+
if name in names:
71+
mapper[name] = obj
72+
return mapper
73+
74+
75+
def map_name_class(names, base):
76+
""" Create map from `schema.table` to class
77+
78+
Parameters
79+
----------
80+
names : list
81+
List of qualified names in `schema.table` format
82+
base : `sqlalchemy.ext.declarative.api.Base`
83+
84+
Sources
85+
-------
86+
https://stackoverflow.com/questions/11668355/sqlalchemy-get-model-from-table-name-this-may-imply-appending-some-function-to
87+
88+
"""
89+
mapper = {}
90+
91+
for cls in Base._decl_class_registry.values():
92+
if hasattr(cls, "__table__"):
93+
name = cls.__table__.schema + "." + cls.__tablename__
94+
if name in names:
95+
mapper[name] = cls
96+
return mapper
97+
98+
99+
def main():
100+
""" """
101+
102+
LOCAL = "postgresql+psycopg2://user:password@localhost:5432/database"
103+
REMOTE = "postgresql+oedialect://openenergy-platform.org"
104+
105+
INPUT = {
106+
"environment.dlm250_geb01_f": None,
107+
"model_draft.wn_abw_ego_dp_hvmv_substation": {"version": "v0.3.0"},
108+
}
109+
110+
source_session = create_session(REMOTE)
111+
target_session = create_session(LOCAL)
112+
113+
schemas = unique_schema(INPUT)
114+
115+
# https://stackoverflow.com/questions/41678073/import-class-from-module-dynamically
116+
117+
# by importing a submodule the class registry
118+
# of `egoio.tools.Base` is extended with
119+
# all classes of the imported submodule
120+
for s in schemas:
121+
packagename = "egoio.db_tables"
122+
import_module(packagename + "." + s)
123+
124+
class_map = map_name_class(INPUT.keys(), Base)
125+
table_map = map_name_table(INPUT.keys(), Base)
126+
127+
# create schema if not exists
128+
# maybe as event hook?
129+
# https://github.com/sqlalchemy/sqlalchemy/issues/3982
130+
for schema in schemas:
131+
query = "CREATE SCHEMA IF NOT EXISTS {schema}".format(schema=schema)
132+
target_session.execute(query)
133+
target_session.commit()
134+
135+
# create subset of database model in local database
136+
metadata = Base.metadata
137+
metadata.bind = target_session.connection().engine
138+
139+
# https://stackoverflow.com/questions/19175311/how-to-create-only-one-table-with-sqlalchemy
140+
metadata.create_all(tables=table_map.values(), checkfirst=True)
141+
142+
# retrieve data from remote database
143+
results = []
144+
145+
for name, v in INPUT.items():
146+
query = source_session.query(class_map[name])
147+
if v:
148+
if "version" in v:
149+
query = query.filter(class_map[name].version == v["version"])
150+
for ds in query:
151+
make_transient(ds)
152+
results.append(ds)
153+
154+
# create WKBElement from WKBElement ;)
155+
# otherwise database error about SRID being 0
156+
for r in results:
157+
for k, v in vars(r).items():
158+
if isinstance(v, WKBElement):
159+
wkb = WKBElement(v.data, srid=v.srid)
160+
setattr(r, k, wkb)
161+
target_session.add(r)
162+
163+
target_session.commit()
164+
source_session.close()
165+
target_session.close()
166+
167+
168+
if __name__ == "__main__":
169+
main()

setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
'workalendar',
1919
'oemof.db',
2020
'demandlib',
21-
'ego.io >=0.0.1rc4, <= 0.0.2',
22-
'geoalchemy2'
21+
'egoio @ git+https://github.com/openego/ego.io.git@features/use-one-Base-definition',
22+
'oedialect @ git+https://github.com/OpenEnergyPlatform/oedialect.git@4957e63ca46a976e35eecf84036466cb624e6bfe',
23+
'geoalchemy2 < 0.7.0',
2324
],
2425
extras_require={
2526
'docs': [

0 commit comments

Comments
 (0)