# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals
from eight import *
from . import Database, databases, Method, methods, Weighting, \
weightings, Normalization, normalizations, preferences, projects
from .backends.peewee import sqlite3_lci_db
from .ia_data_store import abbreviate
from .utils import recursive_str_to_unicode
import numpy as np
import os
import pprint
import pyprind
import re
import shutil
import sqlite3
import warnings
try:
import cPickle as pickle
except ImportError:
import pickle
[docs]
hash_re = re.compile("^[a-zA-Z0-9]{32}$")
[docs]
is_hash = lambda x: bool(hash_re.match(x))
[docs]
UPTODATE_WARNING = "\n\nYour data needs to be updated. Please run the following program on the command line:\n\n\tbw2-uptodate\n"
[docs]
UPDATE_ACTIVITYDATASET = """
BEGIN;
DROP INDEX IF EXISTS "activitydataset_key";
ALTER TABLE ActivityDataset rename to AD_old;
CREATE TABLE "activitydataset" (
"id" INTEGER NOT NULL PRIMARY KEY,
"database" TEXT NOT NULL,
"code" TEXT NOT NULL,
"data" BLOB NOT NULL,
"location" TEXT,
"name" TEXT,
"product" TEXT,
"type" TEXT
);
INSERT INTO ActivityDataset ("database", "code", "data", "location", "name", "product", "type")
SELECT substr(key, 0, instr(key, '⊡')),
substr("key", instr("key", '⊡') + 1),
"data",
"location",
"name",
"product",
"type"
FROM AD_old;
CREATE UNIQUE INDEX "activitydataset_key" ON "activitydataset" ("database", "code");
DROP TABLE AD_old;
COMMIT;
"""
[docs]
UPDATE_EXCHANGEDATASET = """
BEGIN;
DROP INDEX IF EXISTS "exchangedataset_database";
DROP INDEX IF EXISTS "exchangedataset_input";
DROP INDEX IF EXISTS "exchangedataset_output";
ALTER TABLE ExchangeDataset rename to ED_old;
CREATE TABLE "exchangedataset" (
"id" INTEGER NOT NULL PRIMARY KEY,
"data" BLOB NOT NULL,
"input_database" TEXT NOT NULL,
"input_code" TEXT NOT NULL,
"output_database" TEXT NOT NULL,
"output_code" TEXT NOT NULL,
"type" TEXT NOT NULL
);
INSERT INTO ExchangeDataset ("data", "input_database", "input_code", "output_database", "output_code", "type")
SELECT "data",
substr("input", 0, instr("input", '⊡')),
substr("input", instr("input", '⊡') + 1),
substr("output", 0, instr("output", '⊡')),
substr("output", instr("output", '⊡') + 1),
"type"
FROM ED_old;
CREATE INDEX "exchangedataset_input" ON "exchangedataset" ("input_database", "input_code");
CREATE INDEX "exchangedataset_output" ON "exchangedataset" ("output_database", "output_code");
DROP TABLE ED_old;
COMMIT;
"""
[docs]
class Updates(object):
[docs]
UPDATES = {
"2.0 schema change": {
'method': 'schema_change_20_compound_keys',
'explanation': "",
'automatic': True
},
"2.0-2 database search directories": {
'method': "database_search_directories_20",
'automatic': True,
'explanation': ""
},
"2.3 processed data format": {
'method': "processed_data_format_change_23",
'automatic': True,
'explanation': "",
},
}
@classmethod
[docs]
def explain(cls, key):
return cls.UPDATES[key]['explanation']
@classmethod
[docs]
def do_update(cls, key):
method = getattr(cls, cls.UPDATES[key]['method'])
method()
preferences['updates'][key] = True
preferences.flush()
@classmethod
[docs]
def check_status(cls, verbose=True):
"""Check if updates need to be applied.
Returns:
List of needed updates (strings), if any.
"""
cls.set_initial_updates()
updates = sorted([key for key in cls.UPDATES
if not preferences['updates'].get(key)
and not cls.UPDATES[key]['automatic']])
if updates and verbose:
warnings.warn(UPTODATE_WARNING)
return updates
@classmethod
[docs]
def set_initial_updates(cls):
if "updates" in preferences:
return
SQL = "PRAGMA table_info(activitydataset)"
with sqlite3.connect(sqlite3_lci_db.db.database) as conn:
column_names = {x[1] for x in conn.execute(SQL)}
if "code" in column_names:
preferences['updates'] = {key: True for key in cls.UPDATES}
else:
preferences['updates'] = {}
@classmethod
[docs]
def check_automatic_updates(cls):
"""Get list of automatic updates to be applied"""
cls.set_initial_updates()
return sorted([key for key in cls.UPDATES
if not preferences['updates'].get(key)
and cls.UPDATES[key]['automatic']])
@classmethod
[docs]
def reprocess_all_1_0(cls):
"""1.0: Reprocess all to make sure default 'loc' value inserted when not specified."""
objects = [
(methods, Method, "LCIA methods"),
(weightings, Weighting, "LCIA weightings"),
(normalizations, Normalization, "LCIA normalizations"),
(databases, Database, "LCI databases"),
]
for (meta, klass, name) in objects:
if meta.list:
print("Updating all %s" % name)
pbar = pyprind.ProgBar(len(meta), title="Brightway2 {} objects:".format(name), monitor=True)
for index, key in enumerate(meta):
obj = klass(key)
obj.process()
# Free memory
obj = None
pbar.update()
print(pbar)
@classmethod
[docs]
def schema_change_20_compound_keys(cls):
with sqlite3.connect(sqlite3_lci_db.db.database) as conn:
print("Update ActivityDataset table schema and data")
conn.executescript(UPDATE_ACTIVITYDATASET)
print("Updating ExchangeDataset table schema and data")
conn.executescript(UPDATE_EXCHANGEDATASET)
print("Finished with schema change")
@classmethod
[docs]
def database_search_directories_20(cls):
shutil.rmtree(projects.request_directory("whoosh"))
for db in databases:
if databases[db].get('searchable'):
databases[db]['searchable'] = False
print("Reindexing database {}".format(db))
Database(db).make_searchable()
@classmethod