From 83f1e4c18fad790add4e18577d04d3e1ad82a2b8 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 21 Mar 2023 17:31:43 +0100 Subject: [PATCH] add ereuse_utils as module --- ereuse_devicehub/cli.py | 46 +- ereuse_devicehub/client.py | 2 +- ereuse_devicehub/devicehub.py | 6 +- ereuse_devicehub/dummy/dummy.py | 4 +- ereuse_devicehub/ereuse_utils/__init__.py | 173 + ereuse_devicehub/ereuse_utils/cli.py | 301 + ereuse_devicehub/ereuse_utils/cmd.py | 150 + ereuse_devicehub/ereuse_utils/getter.py | 171 + ereuse_devicehub/ereuse_utils/naming.py | 143 + .../ereuse_utils/nested_lookup.py | 88 + ereuse_devicehub/ereuse_utils/session.py | 285 + ereuse_devicehub/ereuse_utils/test.py | 165 + ereuse_devicehub/ereuse_utils/text.py | 72 + .../ereuse_utils/usb_flash_drive.py | 80 + ereuse_devicehub/migrations/script.py.mako | 2 +- ...ef_change_testdatastorage_smallint_for_.py | 31 +- .../versions/21afd375a654_session_table.py | 2 +- .../versions/378b6b147b46_nullable.py | 8 +- .../versions/3a3601ac8224_tradedocuments.py | 226 +- .../versions/4f33137586dd_sanitization.py | 2 +- .../versions/7ecb8ff7abad_documents.py | 131 +- .../968b79fa7756_upgrade_confirmrevoke.py | 10 +- .../versions/b4bd1538bad5_update_live.py | 2 +- .../versions/bf600ca861a4_adding_hid.py | 4 +- .../e93aec8fc41f_added_assigned_action.py | 71 +- .../versions/fbb7e2a0cde0_initial.py | 8116 ++++++++++++++--- ereuse_devicehub/parser/computer.py | 4 +- ereuse_devicehub/parser/utils.py | 2 +- .../resources/action/views/views.py | 4 +- ereuse_devicehub/resources/device/models.py | 2 +- .../resources/inventory/schema.py | 6 +- ereuse_devicehub/resources/tag/__init__.py | 2 +- ereuse_devicehub/teal/client.py | 6 +- ereuse_devicehub/teal/db.py | 2 +- ereuse_devicehub/teal/json_util.py | 4 +- ereuse_devicehub/teal/marshmallow.py | 2 +- ereuse_devicehub/teal/query.py | 2 +- ereuse_devicehub/teal/resource.py | 2 +- ereuse_devicehub/teal/teal.py | 6 +- tests/conftest.py | 2 +- 40 files changed, 8683 insertions(+), 1654 deletions(-) create mode 100644 ereuse_devicehub/ereuse_utils/__init__.py create mode 100644 ereuse_devicehub/ereuse_utils/cli.py create mode 100644 ereuse_devicehub/ereuse_utils/cmd.py create mode 100644 ereuse_devicehub/ereuse_utils/getter.py create mode 100644 ereuse_devicehub/ereuse_utils/naming.py create mode 100644 ereuse_devicehub/ereuse_utils/nested_lookup.py create mode 100644 ereuse_devicehub/ereuse_utils/session.py create mode 100644 ereuse_devicehub/ereuse_utils/test.py create mode 100644 ereuse_devicehub/ereuse_utils/text.py create mode 100644 ereuse_devicehub/ereuse_utils/usb_flash_drive.py diff --git a/ereuse_devicehub/cli.py b/ereuse_devicehub/cli.py index 67054095..e2cec81c 100644 --- a/ereuse_devicehub/cli.py +++ b/ereuse_devicehub/cli.py @@ -2,21 +2,23 @@ import os import click.testing import flask.cli -import ereuse_utils +import ereuse_devicehub.ereuse_utils from ereuse_devicehub.config import DevicehubConfig from ereuse_devicehub.devicehub import Devicehub import sys + sys.ps1 = '\001\033[92m\002>>> \001\033[0m\002' -sys.ps2= '\001\033[94m\002... \001\033[0m\002' +sys.ps2 = '\001\033[94m\002... \001\033[0m\002' import os, readline, atexit + history_file = os.path.join(os.environ['HOME'], '.python_history') try: - readline.read_history_file(history_file) + readline.read_history_file(history_file) except IOError: - pass + pass readline.parse_and_bind("tab: complete") readline.parse_and_bind('"\e[5~": history-search-backward') readline.parse_and_bind('"\e[6~": history-search-forward') @@ -29,6 +31,7 @@ readline.parse_and_bind('"\e[1;5D": backward-word') readline.set_history_length(100000) atexit.register(readline.write_history_file, history_file) + class DevicehubGroup(flask.cli.FlaskGroup): # todo users cannot make cli to use a custom db this way! CONFIG = DevicehubConfig @@ -49,26 +52,37 @@ class DevicehubGroup(flask.cli.FlaskGroup): def get_version(ctx, param, value): if not value or ctx.resilient_parsing: return - click.echo('Devicehub {}'.format(ereuse_utils.version('ereuse-devicehub')), color=ctx.color) + click.echo( + 'Devicehub {}'.format( + ereuse_devicehub.ereuse_utils.version('ereuse-devicehub') + ), + color=ctx.color, + ) flask.cli.get_version(ctx, param, value) -@click.option('--version', - help='Devicehub version.', - expose_value=False, - callback=get_version, - is_flag=True, - is_eager=True) -@click.group(cls=DevicehubGroup, - context_settings=Devicehub.cli_context_settings, - add_version_option=False, - help="""Manages the Devicehub of the inventory {}. +@click.option( + '--version', + help='Devicehub version.', + expose_value=False, + callback=get_version, + is_flag=True, + is_eager=True, +) +@click.group( + cls=DevicehubGroup, + context_settings=Devicehub.cli_context_settings, + add_version_option=False, + help="""Manages the Devicehub of the inventory {}. Use 'export dhi=xx' to set the inventory that this CLI manages. For example 'export dhi=db1' and then executing 'dh tag add' adds a tag in the db1 database. Operations that affect the common database (like creating an user) are not affected by this. - """.format(os.environ.get('dhi'))) + """.format( + os.environ.get('dhi') + ), +) def cli(): pass diff --git a/ereuse_devicehub/client.py b/ereuse_devicehub/client.py index 25d79198..6358aa9e 100644 --- a/ereuse_devicehub/client.py +++ b/ereuse_devicehub/client.py @@ -1,7 +1,7 @@ from inspect import isclass from typing import Dict, Iterable, Type, Union -from ereuse_utils.test import JSON, Res +from ereuse_devicehub.ereuse_utils.test import JSON, Res from flask.testing import FlaskClient from flask_wtf.csrf import generate_csrf from werkzeug.exceptions import HTTPException diff --git a/ereuse_devicehub/devicehub.py b/ereuse_devicehub/devicehub.py index 086895a3..e5e76145 100644 --- a/ereuse_devicehub/devicehub.py +++ b/ereuse_devicehub/devicehub.py @@ -5,8 +5,8 @@ from typing import Type import boltons.urlutils import click import click_spinner -import ereuse_utils.cli -from ereuse_utils.session import DevicehubClient +import ereuse_devicehub.ereuse_utils.cli +from ereuse_devicehub.ereuse_utils.session import DevicehubClient from flask import _app_ctx_stack, g from flask_login import LoginManager, current_user from flask_sqlalchemy import SQLAlchemy @@ -122,7 +122,7 @@ class Devicehub(Teal): @click.option( '--tag-url', '-tu', - type=ereuse_utils.cli.URL(scheme=True, host=True, path=False), + type=ereuse_devicehub.ereuse_utils.cli.URL(scheme=True, host=True, path=False), default='http://example.com', help='The base url (scheme and host) of the tag provider.', ) diff --git a/ereuse_devicehub/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index f3533d48..3769bea1 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -5,10 +5,10 @@ from pathlib import Path import click import click_spinner -import ereuse_utils.cli import jwt import yaml -from ereuse_utils.test import ANY +from ereuse_devicehub.ereuse_utils.test import ANY +from ereuse_devicehub import ereuse_utils from ereuse_devicehub.client import UserClient from ereuse_devicehub.db import db diff --git a/ereuse_devicehub/ereuse_utils/__init__.py b/ereuse_devicehub/ereuse_utils/__init__.py new file mode 100644 index 00000000..0155567f --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/__init__.py @@ -0,0 +1,173 @@ +import enum +import ipaddress +import json +import locale +from collections import Iterable +from datetime import datetime, timedelta +from decimal import Decimal +from distutils.version import StrictVersion +from functools import wraps +from typing import Generator, Union +from uuid import UUID + + +class JSONEncoder(json.JSONEncoder): + """An overloaded JSON Encoder with extra type support.""" + + def default(self, obj): + if isinstance(obj, enum.Enum): + return obj.name + elif isinstance(obj, datetime): + return obj.isoformat() + elif isinstance(obj, timedelta): + return round(obj.total_seconds()) + elif isinstance(obj, UUID): + return str(obj) + elif isinstance(obj, StrictVersion): + return str(obj) + elif isinstance(obj, set): + return list(obj) + elif isinstance(obj, Decimal): + return float(obj) + elif isinstance(obj, Dumpeable): + return obj.dump() + elif isinstance(obj, ipaddress._BaseAddress): + return str(obj) + # Instead of failing, return the string representation by default + return str(obj) + + +class Dumpeable: + """Dumps dictionaries and jsons for Devicehub. + + A base class to allow subclasses to generate dictionaries + and json suitable for sending to a Devicehub, i.e. preventing + private and constants to be in the JSON and camelCases field names. + """ + + ENCODER = JSONEncoder + + def dump(self): + """ + Creates a dictionary consisting of the + non-private fields of this instance with camelCase field names. + """ + import inflection + + return { + inflection.camelize(name, uppercase_first_letter=False): getattr(self, name) + for name in self._field_names() + if not name.startswith('_') and not name[0].isupper() + } + + def _field_names(self): + """An iterable of the names to dump.""" + # Feel free to override this + return vars(self).keys() + + def to_json(self): + """ + Creates a JSON representation of the non-private fields of + this class. + """ + return json.dumps(self, cls=self.ENCODER, indent=2) + + +class DumpeableModel(Dumpeable): + """A dumpeable for SQLAlchemy models. + + Note that this does not avoid recursive relations. + """ + + def _field_names(self): + from sqlalchemy import inspect + + return (a.key for a in inspect(self).attrs) + + +def ensure_utf8(app_name_to_show_on_error: str): + """ + Python3 uses by default the system set, but it expects it to be + ‘utf-8’ to work correctly. + This can generate problems in reading and writing files and in + ``.decode()`` method. + + An example how to 'fix' it:: + + echo 'export LC_CTYPE=en_US.UTF-8' > .bash_profile + echo 'export LC_ALL=en_US.UTF-8' > .bash_profile + """ + encoding = locale.getpreferredencoding() + if encoding.lower() != 'utf-8': + raise OSError( + '{} works only in UTF-8, but yours is set at {}' + ''.format(app_name_to_show_on_error, encoding) + ) + + +def now() -> datetime: + """ + Returns a compatible 'now' with DeviceHub's API, + this is as UTC and without microseconds. + """ + return datetime.utcnow().replace(microsecond=0) + + +def flatten_mixed(values: Iterable) -> Generator: + """ + Flatten a list containing lists and other elements. This is not deep. + + >>> list(flatten_mixed([1, 2, [3, 4]])) + [1, 2, 3, 4] + """ + for x in values: + if isinstance(x, list): + for y in x: + yield y + else: + yield x + + +def if_none_return_none(f): + """If the first value is None return None, otherwise execute f.""" + + @wraps(f) + def wrapper(self, value, *args, **kwargs): + if value is None: + return None + return f(self, value, *args, **kwargs) + + return wrapper + + +def local_ip( + dest='109.69.8.152', +) -> Union[ipaddress.IPv4Address, ipaddress.IPv6Address]: + """Gets the local IP of the interface that has access to the + Internet. + + This is a reliable way to test if a device has an active + connection to the Internet. + + This method works by connecting, by default, + to the IP of ereuse01.ereuse.org. + + >>> local_ip() + + :raise OSError: The device cannot connect to the Internet. + """ + import socket, ipaddress + + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect((dest, 80)) + ip = s.getsockname()[0] + s.close() + return ipaddress.ip_address(ip) + + +def version(package_name: str) -> StrictVersion: + """Returns the version of a package name installed with pip.""" + # From https://stackoverflow.com/a/2073599 + import pkg_resources + + return StrictVersion(pkg_resources.require(package_name)[0].version) diff --git a/ereuse_devicehub/ereuse_utils/cli.py b/ereuse_devicehub/ereuse_utils/cli.py new file mode 100644 index 00000000..d4df20bf --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/cli.py @@ -0,0 +1,301 @@ +import enum as _enum +import getpass +import itertools +import os +import pathlib +import threading +from contextlib import contextmanager +from time import sleep +from typing import Any, Iterable, Type + +from boltons import urlutils +from click import types as click_types +from colorama import Fore +from tqdm import tqdm + +from ereuse_devicehub.ereuse_utils import if_none_return_none + +COMMON_CONTEXT_S = {'help_option_names': ('-h', '--help')} +"""Common Context settings used for our implementations of the +Click cli. +""" + +# Py2/3 compat. Empty conditional to avoid coverage +try: + _unicode = unicode +except NameError: + _unicode = str + + +class Enum(click_types.Choice): + """ + Enum support for click. + + Use it as a collection: @click.option(..., type=cli.Enum(MyEnum)). + Then, this expects you to pass the *name* of a member of the enum. + + From `this github issue `_. + """ + + def __init__(self, enum: Type[_enum.Enum]): + self.__enum = enum + super().__init__(enum.__members__) + + def convert(self, value, param, ctx): + return self.__enum[super().convert(value, param, ctx)] + + +class Path(click_types.Path): + """Like click.Path but returning ``pathlib.Path`` objects.""" + + def convert(self, value, param, ctx): + return pathlib.Path(super().convert(value, param, ctx)) + + +class URL(click_types.StringParamType): + """Returns a bolton's URL.""" + + name = 'url' + + def __init__( + self, + scheme=None, + username=None, + password=None, + host=None, + port=None, + path=None, + query_params=None, + fragment=None, + ) -> None: + super().__init__() + """Creates the type URL. You can require or enforce parts + of the URL by setting parameters of this constructor. + + If the param is... + + - None, no check is performed (default). + - True, it is then required as part of the URL. + - False, it is then required NOT to be part of the URL. + - Any other value, then such value is required to be in + the URL. + """ + self.attrs = ( + ('scheme', scheme), + ('username', username), + ('password', password), + ('host', host), + ('port', port), + ('path', path), + ('query_params', query_params), + ('fragment', fragment), + ) + + @if_none_return_none + def convert(self, value, param, ctx): + url = urlutils.URL(super().convert(value, param, ctx)) + for name, attr in self.attrs: + if attr is True: + if not getattr(url, name): + self.fail( + 'URL {} must contain {} but it does not.'.format(url, name) + ) + elif attr is False: + if getattr(url, name): + self.fail('URL {} cannot contain {} but it does.'.format(url, name)) + elif attr: + if getattr(url, name) != attr: + self.fail('{} form {} can only be {}'.format(name, url, attr)) + return url + + +def password(service: str, username: str, prompt: str = 'Password:') -> str: + """Gets a password from the keyring or the terminal.""" + import keyring + + return keyring.get_password(service, username) or getpass.getpass(prompt) + + +class Line(tqdm): + spinner_cycle = itertools.cycle(['-', '/', '|', '\\']) + + def __init__( + self, + total=None, + desc=None, + leave=True, + file=None, + ncols=None, + mininterval=0.2, + maxinterval=10.0, + miniters=None, + ascii=None, + disable=False, + unit='it', + unit_scale=False, + dynamic_ncols=True, + smoothing=0.3, + bar_format=None, + initial=0, + position=None, + postfix=None, + unit_divisor=1000, + write_bytes=None, + gui=False, + close_message: Iterable = None, + error_message: Iterable = None, + **kwargs, + ): + """This cannot work with iterables. Iterable use is considered + backward-compatibility in tqdm and inconsistent in Line. + Manually call ``update``. + """ + self._close_message = close_message + self._error_message = error_message + if total: + bar_format = '{desc}{percentage:.1f}% |{bar}| {n:1g}/{total:1g} {elapsed}<{remaining}' + super().__init__( + None, + desc, + total, + leave, + file, + ncols, + mininterval, + maxinterval, + miniters, + ascii, + disable, + unit, + unit_scale, + dynamic_ncols, + smoothing, + bar_format, + initial, + position, + postfix, + unit_divisor, + write_bytes, + gui, + **kwargs, + ) + + def write_at_line(self, *args): + self.clear() + with self._lock: + self.display(''.join(str(arg) for arg in args)) + + def close_message(self, *args): + self._close_message = args + + def error_message(self, *args): + self._error_message = args + + def close(self): + """ + Cleanup and (if leave=False) close the progressbar. + """ + if self.disable: + return + + # Prevent multiple closures + self.disable = True + + # decrement instance pos and remove from internal set + pos = abs(self.pos) + self._decr_instances(self) + + # GUI mode + if not hasattr(self, "sp"): + return + + # annoyingly, _supports_unicode isn't good enough + def fp_write(s): + self.fp.write(_unicode(s)) + + try: + fp_write('') + except ValueError as e: + if 'closed' in str(e): + return + raise # pragma: no cover + + with self._lock: + if self.leave: + if self._close_message: + self.display( + ''.join(str(arg) for arg in self._close_message), pos=pos + ) + elif self.last_print_n < self.n: + # stats for overall rate (no weighted average) + self.avg_time = None + self.display(pos=pos) + if not max( + [abs(getattr(i, "pos", 0)) for i in self._instances] + [pos] + ): + # only if not nested (#477) + fp_write('\n') + else: + if self._close_message: + self.display( + ''.join(str(arg) for arg in self._close_message), pos=pos + ) + else: + self.display(msg='', pos=pos) + if not pos: + fp_write('\r') + + @contextmanager + def spin(self, prefix: str): + self._stop_running = threading.Event() + spin_thread = threading.Thread(target=self._spin, args=[prefix]) + spin_thread.start() + try: + yield + finally: + self._stop_running.set() + spin_thread.join() + + def _spin(self, prefix: str): + while not self._stop_running.is_set(): + self.write_at_line(prefix, next(self.spinner_cycle)) + sleep(0.50) + + @classmethod + @contextmanager + def reserve_lines(self, n): + try: + yield + finally: + self.move_down(n - 1) + + @classmethod + def move_down(cls, n: int): + print('\n' * n) + + def __exit__(self, *exc): + if exc[0]: + self._close_message = self._error_message + return super().__exit__(*exc) + + +def clear(): + os.system('clear') + + +def title(text: Any, ljust=32) -> str: + # Note that is 38 px + 1 extra space = 39 min + return str(text).ljust(ljust) + ' ' + + +def danger(text: Any) -> str: + return '{}{}{}'.format(Fore.RED, text, Fore.RESET) + + +def warning(text: Any) -> str: + return '{}{}{}'.format(Fore.YELLOW, text, Fore.RESET) + + +def done(text: Any = 'done.') -> str: + return '{}{}{}'.format(Fore.GREEN, text, Fore.RESET) diff --git a/ereuse_devicehub/ereuse_utils/cmd.py b/ereuse_devicehub/ereuse_utils/cmd.py new file mode 100644 index 00000000..2f03807f --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/cmd.py @@ -0,0 +1,150 @@ +import subprocess +from contextlib import suppress +from typing import Any, Set, TextIO + +from ereuse_devicehub.ereuse_utils import text + + +def run( + *cmd: Any, + out=subprocess.PIPE, + err=subprocess.DEVNULL, + to_string=True, + check=True, + shell=False, + **kwargs, +) -> subprocess.CompletedProcess: + """subprocess.run with a better API. + + :param cmd: A list of commands to execute as parameters. + Parameters will be passed-in to ``str()`` so they + can be any object that can handle str(). + :param out: As ``subprocess.run.stdout``. + :param err: As ``subprocess.run.stderr``. + :param to_string: As ``subprocess.run.universal_newlines``. + :param check: As ``subprocess.run.check``. + :param shell: + :param kwargs: Any other parameters that ``subprocess.run`` + accepts. + :return: The result of executing ``subprocess.run``. + """ + cmds = tuple(str(c) for c in cmd) + return subprocess.run( + ' '.join(cmds) if shell else cmds, + stdout=out, + stderr=err, + universal_newlines=to_string, + check=check, + shell=shell, + **kwargs, + ) + + +class ProgressiveCmd: + """Executes a cmd while interpreting its completion percentage. + + The completion percentage of the cmd is stored in + :attr:`.percentage` and the user can obtain percentage + increments by executing :meth:`.increment`. + + This class is useful to use within a child thread, so a main + thread can request from time to time the percentage / increment + status of the running command. + """ + + READ_LINE = None + DECIMALS = {4, 5, 6} + DECIMAL_NUMBERS = 2 + INT = {1, 2, 3} + + def __init__( + self, + *cmd: Any, + stdout=subprocess.DEVNULL, + number_chars: Set[int] = INT, + decimal_numbers: int = None, + read: int = READ_LINE, + callback=None, + check=True, + ): + """ + :param cmd: The command to execute. + :param stderr: the stderr passed-in to Popen. + :param stdout: the stdout passed-in to Popen + :param number_chars: The number of chars used to represent + the percentage. Normalized cases are + :attr:`.DECIMALS` and :attr:`.INT`. + :param read: For commands that do not print lines, how many + characters we should read between updates. + The percentage should be between those + characters. + :param callback: If passed in, this method is executed every time + run gets an update from the command, passing + in the increment from the last execution. + If not passed-in, you can get such increment + by executing manually the ``increment`` method. + :param check: Raise error if subprocess return code is non-zero. + """ + self.cmd = tuple(str(c) for c in cmd) + self.read = read + self.step = 0 + self.check = check + self.number_chars = number_chars + self.decimal_numbers = decimal_numbers + # We call subprocess in the main thread so the main thread + # can react on ``CalledProcessError`` exceptions + self.conn = conn = subprocess.Popen( + self.cmd, universal_newlines=True, stderr=subprocess.PIPE, stdout=stdout + ) + self.out = ( + conn.stdout if stdout == subprocess.PIPE else conn.stderr + ) # type: TextIO + self._callback = callback + self.last_update_percentage = 0 + self.percentage = 0 + + @property + def percentage(self): + return self._percentage + + @percentage.setter + def percentage(self, v): + self._percentage = v + if self._callback and self._percentage > 0: + increment = self.increment() + if ( + increment > 0 + ): # Do not bother calling if there has not been any increment + self._callback(increment, self._percentage) + + def run(self) -> None: + """Processes the output.""" + while True: + out = self.out.read(self.read) if self.read else self.out.readline() + if out: + with suppress(StopIteration): + self.percentage = next( + text.positive_percentages( + out, self.number_chars, self.decimal_numbers + ) + ) + else: # No more output + break + return_code = self.conn.wait() # wait until cmd ends + if self.check and return_code != 0: + raise subprocess.CalledProcessError( + self.conn.returncode, self.conn.args, stderr=self.conn.stderr.read() + ) + + def increment(self): + """Returns the increment of progression from + the last time this method is executed. + """ + # for cmd badblocks the increment can be negative at the + # beginning of the second step where last_percentage + # is 100 and percentage is 0. By using max we + # kind-of reset the increment and start counting for + # the second step + increment = max(self.percentage - self.last_update_percentage, 0) + self.last_update_percentage = self.percentage + return increment diff --git a/ereuse_devicehub/ereuse_utils/getter.py b/ereuse_devicehub/ereuse_utils/getter.py new file mode 100644 index 00000000..b9ffd71d --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/getter.py @@ -0,0 +1,171 @@ +"""Functions to get values from dictionaries and list encoded key-value +strings with meaningful indentations. + +Values obtained from these functions are sanitized and automatically +(or explicitly set) casted. Sanitization includes removing unnecessary +whitespaces and removing useless keywords (in the context of +computer hardware) from the texts. +""" + +import re +from itertools import chain +from typing import Any, Iterable, Set, Type, Union +from unittest.mock import DEFAULT + +import boltons.iterutils +import yaml + +from ereuse_devicehub.ereuse_utils.text import clean + + +def dict( + d: dict, + path: Union[str, tuple], + remove: Set[str] = set(), + default: Any = DEFAULT, + type: Type = None, +): + """Gets a value from the dictionary and sanitizes it. + + Values are patterned and compared against sets + of meaningless characters for device hardware. + + :param d: A dictionary potentially containing the value. + :param path: The key or a tuple-path where the value should be. + :param remove: Remove these words if found. + :param default: A default value to return if not found. If not set, + an exception is raised. + :param type: Enforce a type on the value (like ``int``). By default + dict tries to guess the correct type. + """ + try: + v = boltons.iterutils.get_path(d, (path,) if isinstance(path, str) else path) + except KeyError: + return _default(path, default) + else: + return sanitize(v, remove, type=type) + + +def kv( + iterable: Iterable[str], + key: str, + default: Any = DEFAULT, + sep=':', + type: Type = None, +) -> Any: + """Key-value. Gets a value from an iterable representing key values in the + form of a list of strings lines, for example an ``.ini`` or yaml file, + if they are opened with ``.splitlines()``. + + :param iterable: An iterable of strings. + :param key: The key where the value should be. + :param default: A default value to return if not found. If not set, + an exception is raised. + :param sep: What separates the key from the value in the line. + Usually ``:`` or ``=``. + :param type: Enforce a type on the value (like ``int``). By default + dict tries to guess the correct type. + """ + for line in iterable: + try: + k, value, *_ = line.strip().split(sep) + except ValueError: + continue + else: + if key == k: + return sanitize(value, type=type) + return _default(key, default) + + +def indents(iterable: Iterable[str], keyword: str, indent=' '): + """For a given iterable of strings, returns blocks of the same + left indentation. + + For example: + foo1 + bar1 + bar2 + foo2 + foo2 + + For that text, this method would return ``[bar1, bar2]`` for passed-in + keyword ``foo1``. + + :param iterable: A list of strings representing lines. + :param keyword: The title preceding the indentation. + :param indent: Which characters makes the indentation. + """ + section_pos = None + for i, line in enumerate(iterable): + if not line.startswith(indent): + if keyword in line: + section_pos = i + elif section_pos is not None: + yield iterable[section_pos:i] + section_pos = None + return + + +def _default(key, default): + if default is DEFAULT: + raise IndexError('Value {} not found.'.format(key)) + else: + return default + + +"""Gets""" +TO_REMOVE = {'none', 'prod', 'o.e.m', 'oem', r'n/a', 'atapi', 'pc', 'unknown'} +"""Delete those *words* from the value""" +assert all(v.lower() == v for v in TO_REMOVE), 'All words need to be lower-case' + +REMOVE_CHARS_BETWEEN = '(){}[]' +""" +Remove those *characters* from the value. +All chars inside those are removed. Ex: foo (bar) => foo +""" +CHARS_TO_REMOVE = '*' +"""Remove the characters. + +'*' Needs to be removed or otherwise it is interpreted +as a glob expression by regexes. +""" + +MEANINGLESS = { + 'to be filled', + 'system manufacturer', + 'system product', + 'sernum', + 'xxxxx', + 'system name', + 'not specified', + 'modulepartnumber', + 'system serial', + '0001-067a-0000', + 'partnum', + 'manufacturer', + '0000000', + 'fffff', + 'jedec id:ad 00 00 00 00 00 00 00', + '012000', + 'x.x', + 'sku', +} +"""Discard a value if any of these values are inside it. """ +assert all(v.lower() == v for v in MEANINGLESS), 'All values need to be lower-case' + + +def sanitize(value, remove=set(), type=None): + if value is None: + return None + remove = remove | TO_REMOVE + regex = r'({})\W'.format('|'.join(s for s in remove)) + val = re.sub(regex, '', value, flags=re.IGNORECASE) + val = '' if val.lower() in remove else val # regex's `\W` != whole string + val = re.sub(r'\([^)]*\)', '', val) # Remove everything between + for char_to_remove in chain(REMOVE_CHARS_BETWEEN, CHARS_TO_REMOVE): + val = val.replace(char_to_remove, '') + val = clean(val) + if val and not any(meaningless in val.lower() for meaningless in MEANINGLESS): + return type(val) if type else yaml.load(val, Loader=yaml.SafeLoader) + else: + return None diff --git a/ereuse_devicehub/ereuse_utils/naming.py b/ereuse_devicehub/ereuse_utils/naming.py new file mode 100644 index 00000000..32371f25 --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/naming.py @@ -0,0 +1,143 @@ +from inflection import ( + camelize, + dasherize, + parameterize, + pluralize, + singularize, + underscore, +) + +HID_CONVERSION_DOC = """ + The HID is the result of concatenating, + in the following order: the type of device (ex. Computer), + the manufacturer name, the model name, and the S/N. It is joined + with hyphens, and adapted to comply with the URI specification, so + it can be used in the URI identifying the device on the Internet. + The conversion is done as follows: + + 1. non-ASCII characters are converted to their ASCII equivalent or + removed. + 2. Characterst that are not letters or numbers are converted to + underscores, in a way that there are no trailing underscores + and no underscores together, and they are set to lowercase. + + Ex. ``laptop-acer-aod270-lusga_0d0242201212c7614`` + """ + + +class Naming: + """ + In DeviceHub there are many ways to name the same resource (yay!), this is because of all the different + types of schemas we work with. But no worries, we offer easy ways to change between naming conventions. + + - TypeCase (or resource-type) is the one represented with '@type' and follow PascalCase and always singular. + This is the standard preferred one. + - resource-case is the eve naming, using the standard URI conventions. This one is tricky, as although the types + are represented in singular, the URI convention is to be plural (Event vs events), however just few of them + follow this rule (Snapshot [type] to snapshot [resource]). You can set which ones you want to change their + number. + - python_case is the one used by python for its folders and modules. It is underscored and always singular. + """ + + TYPE_PREFIX = ':' + RESOURCE_PREFIX = '_' + + @staticmethod + def resource(string: str): + """ + :param string: String can be type, resource or python case + """ + try: + prefix, resulting_type = Naming.pop_prefix(string) + prefix += Naming.RESOURCE_PREFIX + except IndexError: + prefix = '' + resulting_type = string + resulting_type = dasherize(underscore(resulting_type)) + return prefix + pluralize(resulting_type) + + @staticmethod + def python(string: str): + """ + :param string: String can be type, resource or python case + """ + return underscore(singularize(string)) + + @staticmethod + def type(string: str): + try: + prefix, resulting_type = Naming.pop_prefix(string) + prefix += Naming.TYPE_PREFIX + except IndexError: + prefix = '' + resulting_type = string + resulting_type = singularize(resulting_type) + resulting_type = resulting_type.replace( + '-', '_' + ) # camelize does not convert '-' but '_' + return prefix + camelize(resulting_type) + + @staticmethod + def url_word(word: str): + """ + Normalizes a full word to be inserted to an url. If the word has spaces, etc, is used '_' and not '-' + """ + return parameterize(word, '_') + + @staticmethod + def pop_prefix(string: str): + """Erases the prefix and returns it. + :throws IndexError: There is no prefix. + :return A set with two elements: 1- the prefix, 2- the type without it. + """ + result = string.split(Naming.TYPE_PREFIX) + if len(result) == 1: + result = string.split(Naming.RESOURCE_PREFIX) + if len(result) == 1: + raise IndexError() + return result + + @staticmethod + def new_type(type_name: str, prefix: str or None = None) -> str: + """ + Creates a resource type with optionally a prefix. + + Using the rules of JSON-LD, we use prefixes to disambiguate between different types with the same name: + one can Accept a device or a project. In eReuse.org there are different events with the same names, in + linked-data terms they have different URI. In eReuse.org, we solve this with the following: + + "@type": "devices:Accept" // the URI for these events is 'devices/events/accept' + "@type": "projects:Accept" // the URI for these events is 'projects/events/accept + ... + + Type is only used in events, when there are ambiguities. The rest of + + "@type": "devices:Accept" + "@type": "Accept" + + But these not: + + "@type": "projects:Accept" // it is an event from a project + "@type": "Accept" // it is an event from a device + """ + if Naming.TYPE_PREFIX in type_name: + raise TypeError( + 'Cannot create new type: type {} is already prefixed.'.format(type_name) + ) + prefix = (prefix + Naming.TYPE_PREFIX) if prefix is not None else '' + return prefix + type_name + + @staticmethod + def hid(type: str, manufacturer: str, model: str, serial_number: str) -> str: + ( + """Computes the HID for the given properties of a device. + The HID is suitable to use to an URI. + """ + + HID_CONVERSION_DOC + ) + return '{type}-{mn}-{ml}-{sn}'.format( + type=Naming.url_word(type), + mn=Naming.url_word(manufacturer), + ml=Naming.url_word(model), + sn=Naming.url_word(serial_number), + ) diff --git a/ereuse_devicehub/ereuse_utils/nested_lookup.py b/ereuse_devicehub/ereuse_utils/nested_lookup.py new file mode 100644 index 00000000..f2a99bc9 --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/nested_lookup.py @@ -0,0 +1,88 @@ +from typing import Generator + + +class NestedLookup: + @staticmethod + def __new__(cls, document, references, operation): + """Lookup a key in a nested document, return a list of values + From https://github.com/russellballestrini/nested-lookup/ but in python 3 + """ + return list(NestedLookup._nested_lookup(document, references, operation)) + + @staticmethod + def key_equality_factory(key_to_find): + def key_equality(key, _): + return key == key_to_find + + return key_equality + + @staticmethod + def is_sub_type_factory(type): + def _is_sub_type(_, value): + return is_sub_type(value, type) + + return _is_sub_type + + @staticmethod + def key_value_equality_factory(key_to_find, value_to_find): + def key_value_equality(key, value): + return key == key_to_find and value == value_to_find + + return key_value_equality + + @staticmethod + def key_value_containing_value_factory(key_to_find, value_to_find): + def key_value_containing_value(key, value): + return key == key_to_find and value_to_find in value + + return key_value_containing_value + + @staticmethod + def _nested_lookup(document, references, operation): + """Lookup a key in a nested document, yield a value""" + if isinstance(document, list): + for d in document: + for result in NestedLookup._nested_lookup(d, references, operation): + yield result + + if isinstance(document, dict): + for k, v in document.items(): + if operation(k, v): + references.append((document, k)) + yield v + elif isinstance(v, dict): + for result in NestedLookup._nested_lookup(v, references, operation): + yield result + elif isinstance(v, list): + for d in v: + for result in NestedLookup._nested_lookup( + d, references, operation + ): + yield result + + +def is_sub_type(value, resource_type): + try: + return issubclass(value, resource_type) + except TypeError: + return issubclass(value.__class__, resource_type) + + +def get_nested_dicts_with_key_value(parent_dict: dict, key, value): + """Return all nested dictionaries that contain a key with a specific value. A sub-case of NestedLookup.""" + references = [] + NestedLookup( + parent_dict, references, NestedLookup.key_value_equality_factory(key, value) + ) + return (document for document, _ in references) + + +def get_nested_dicts_with_key_containing_value(parent_dict: dict, key, value): + """Return all nested dictionaries that contain a key with a specific value. A sub-case of NestedLookup.""" + references = [] + NestedLookup( + parent_dict, + references, + NestedLookup.key_value_containing_value_factory(key, value), + ) + return (document for document, _ in references) diff --git a/ereuse_devicehub/ereuse_utils/session.py b/ereuse_devicehub/ereuse_utils/session.py new file mode 100644 index 00000000..647c7634 --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/session.py @@ -0,0 +1,285 @@ +import base64 +import json +from typing import Any, Dict, Iterable, Tuple, TypeVar, Union + +import boltons.urlutils +from requests import Response +from requests_toolbelt.sessions import BaseUrlSession +from urllib3 import Retry + +from ereuse_devicehub import ereuse_utils + +# mypy +Query = Iterable[Tuple[str, Any]] + +Status = Union[int] + +try: + from typing import Protocol # Only py 3.6+ +except ImportError: + pass +else: + class HasStatusProperty(Protocol): + def __init__(self, *args, **kwargs) -> None: + self.status = ... # type: int + + + Status = Union[int, HasStatusProperty] + +JSON = 'application/json' +ANY = '*/*' +AUTH = 'Authorization' +BASIC = 'Basic {}' +URL = Union[str, boltons.urlutils.URL] +Data = Union[str, dict, ereuse_utils.Dumpeable] +Res = Tuple[Union[Dict[str, Any], str], Response] + + +# actual code + + +class Session(BaseUrlSession): + """A BaseUrlSession that always raises for status and sets a + timeout for all requests by default. + """ + + def __init__(self, base_url=None, timeout=15): + """ + :param base_url: + :param timeout: Time requests will wait to receive the first + response bytes (not the whole) from the server. In seconds. + """ + super().__init__(base_url) + self.timeout = timeout + self.hooks['response'] = lambda r, *args, **kwargs: r.raise_for_status() + + def request(self, method, url, *args, **kwargs): + kwargs.setdefault('timeout', self.timeout) + return super().request(method, url, *args, **kwargs) + + def __repr__(self): + return '<{} base={}>.'.format(self.__class__.__name__, self.base_url) + + +class DevicehubClient(Session): + """A Session pre-configured to connect to Devicehub-like APIs.""" + + def __init__(self, base_url: URL = None, + token: str = None, + inventory: Union[str, bool] = False, + **kwargs): + """Initializes a session pointing to a Devicehub endpoint. + + Authentication can be passed-in as a token for endpoints + that require them, now at ini, after when executing the method, + or in between with ``set_auth``. + + :param base_url: An url pointing to a endpoint. + :param token: A Base64 encoded token, as given by a devicehub. + You can encode tokens by executing `encode_token`. + :param inventory: If True, use the default inventory of the user. + If False, do not use inventories (single-inventory + database, this is the option by default). + If a string, always use the set inventory. + """ + if isinstance(base_url, boltons.urlutils.URL): + base_url = base_url.to_text() + else: + base_url = str(base_url) + super().__init__(base_url, **kwargs) + assert base_url[-1] != '/', 'Do not provide a final slash to the URL' + if token: + self.set_auth(token) + self.inventory = inventory + self.user = None # type: Dict[str, object] + + def set_auth(self, token): + self.headers['Authorization'] = 'Basic {}'.format(token) + + @classmethod + def encode_token(cls, token: str): + """Encodes a token suitable for a Devicehub endpoint.""" + return base64.b64encode(str.encode(str(token) + ':')).decode() + + def login(self, email: str, password: str) -> Dict[str, Any]: + """Performs login, authenticating future requests. + + :return: The logged-in user. + """ + user, _ = self.post('/users/login/', {'email': email, 'password': password}, status=200) + self.set_auth(user['token']) + self.user = user + self.inventory = user['inventories'][0]['id'] + return user + + def get(self, + base_url: URL, + uri=None, + status: Status = 200, + query: Query = tuple(), + accept=JSON, + content_type=JSON, + headers: dict = None, + token=None, + **kwargs) -> Res: + return super().get(base_url, + uri=uri, + status=status, + query=query, + accept=accept, + content_type=content_type, + headers=headers, + token=token, **kwargs) + + def post(self, base_url: URL, + data: Data, + uri=None, + status: Status = 201, + query: Query = tuple(), + accept=JSON, + content_type=JSON, + headers: dict = None, + token=None, + **kwargs) -> Res: + return super().post(base_url, + data=data, + uri=uri, + status=status, + query=query, + accept=accept, + content_type=content_type, + headers=headers, + token=token, **kwargs) + + def delete(self, + base_url: URL, + uri=None, + status: Status = 204, + query: Query = tuple(), + accept=JSON, + content_type=JSON, + headers: dict = None, + token=None, + **kwargs) -> Res: + return super().delete(base_url, + uri=uri, + status=status, + query=query, + accept=accept, + content_type=content_type, + headers=headers, + token=token, **kwargs) + + def patch(self, base_url: URL, + data: Data, + uri=None, + status: Status = 201, + query: Query = tuple(), + accept=JSON, + content_type=JSON, + headers: dict = None, + token=None, + **kwargs) -> Res: + return super().patch(base_url, + data=data, + uri=uri, + status=status, + query=query, + accept=accept, + content_type=content_type, + headers=headers, + token=token, **kwargs) + + def request(self, + method, + base_url: URL, + uri=None, + status: Status = 200, + query: Query = tuple(), + accept=JSON, + content_type=JSON, + data=None, + headers: dict = None, + token=None, + **kw) -> Res: + assert not kw.get('json', None), 'Do not use json; use data.' + # We allow uris without slashes for item endpoints + uri = str(uri) if uri else None + headers = headers or {} + headers['Accept'] = accept + headers['Content-Type'] = content_type + if token: + headers['Authorization'] = 'Basic {}'.format(token) + if data and content_type == JSON: + data = json.dumps(data, cls=ereuse_utils.JSONEncoder, sort_keys=True) + url = base_url if not isinstance(base_url, boltons.urlutils.URL) else base_url.to_text() + assert url[-1] == '/', 'base_url should end with a slash' + if self.inventory and not isinstance(self.inventory, bool): + url = '{}/{}'.format(self.inventory, base_url) + assert url[-1] == '/', 'base_url should end with a slash' + if uri: + url = self.parse_uri(url, uri) + if query: + url = self.parse_query(url, query) + response = super().request(method, url, data=data, headers=headers, **kw) + if status: + _status = getattr(status, 'code', status) + if _status != response.status_code: + raise WrongStatus('Req to {} failed bc the status is {} but it should have been {}' + .format(url, response.status_code, _status)) + data = response.content if not accept == JSON or not response.content else response.json() + return data, response + + @staticmethod + def parse_uri(base_url, uri): + return boltons.urlutils.URL(base_url).navigate(uri).to_text() + + @staticmethod + def parse_query(uri, query): + url = boltons.urlutils.URL(uri) + url.query_params = boltons.urlutils.QueryParamDict([ + (k, json.dumps(v, cls=ereuse_utils.JSONEncoder) if isinstance(v, (list, dict)) else v) + for k, v in query + ]) + return url.to_text() + + def __repr__(self): + return '<{} base={} inv={} user={}>.'.format(self.__class__.__name__, self.base_url, + self.inventory, self.user) + + +class WrongStatus(Exception): + pass + + +import requests +from requests.adapters import HTTPAdapter + +T = TypeVar('T', bound=requests.Session) + + +def retry(session: T, + retries=3, + backoff_factor=1, + status_to_retry=(500, 502, 504)) -> T: + """Configures requests from the given session to retry in + failed requests due to connection errors, HTTP response codes + with ``status_to_retry`` and 30X redirections. + + Remember that you still need + """ + # From https://www.peterbe.com/plog/best-practice-with-retries-with-requests + # Doc in https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html#module-urllib3.util.retry + session = session or requests.Session() + retry = Retry( + total=retries, + read=retries, + connect=retries, + backoff_factor=backoff_factor, + status_forcelist=status_to_retry, + method_whitelist=False # Retry too in non-idempotent methods like POST + ) + adapter = HTTPAdapter(max_retries=retry) + session.mount('http://', adapter) + session.mount('https://', adapter) + return session diff --git a/ereuse_devicehub/ereuse_utils/test.py b/ereuse_devicehub/ereuse_utils/test.py new file mode 100644 index 00000000..b78631b2 --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/test.py @@ -0,0 +1,165 @@ +from contextlib import suppress +from typing import Dict, Tuple, Union + +from flask import json +from flask.testing import FlaskClient +from werkzeug.wrappers import Response + +from ereuse_devicehub.ereuse_utils.session import ANY, AUTH, BASIC, DevicehubClient, JSON, Query, Status + +ANY = ANY +AUTH = AUTH +BASIC = BASIC + +Res = Tuple[Union[Dict[str, object], str], Response] + + +class Client(FlaskClient): + """ + A client for the REST servers of DeviceHub and WorkbenchServer. + + - JSON first. By default it sends and expects receiving JSON files. + - Assert regular status responses, like 200 for GET. + - Auto-parses a nested dictionary of URL query params to the + URL version with nested properties to JSON. + - Meaningful headers format: a dictionary of name-values. + """ + + def open(self, + uri: str, + status: Status = 200, + query: Query = tuple(), + accept=JSON, + content_type=JSON, + item=None, + headers: dict = None, + **kw) -> Res: + """ + + :param uri: The URI without basename and query. + :param status: Assert the response for specified status. Set + None to avoid. + :param query: The query of the URL in the form of + [(key1, value1), (key2, value2), (key1, value3)]. + If value is a list or a dict, they will be + converted to JSON. + Please, see :class:`boltons.urlutils`. + QueryParamDict` for more info. + :param accept: The Accept header. If 'application/json' + (default) then it will parse incoming JSON. + :param item: The last part of the path. Useful to do something + like ``get('db/accounts', item='24')``. If you + use ``item``, you can't set a final backslash into + ``uri`` (or the parse will fail). + :param headers: A dictionary of headers, where keys are header + names and values their values. + Ex: {'Accept', 'application/json'}. + :param kw: Kwargs passed into parent ``open``. + :return: A tuple with: 1. response data, as a string or JSON + depending of Accept, and 2. the Response object. + """ + j_encoder = self.application.json_encoder + headers = headers or {} + headers['Accept'] = accept + headers['Content-Type'] = content_type + headers = [(k, v) for k, v in headers.items()] + if 'data' in kw and content_type == JSON: + kw['data'] = json.dumps(kw['data'], cls=j_encoder) + if item: + uri = DevicehubClient.parse_uri(uri, item) + if query: + uri = DevicehubClient.parse_query(uri, query) + response = super().open(uri, headers=headers, **kw) + if status: + _status = getattr(status, 'code', status) + assert response.status_code == _status, \ + 'Expected status code {} but got {}. Returned data is:\n' \ + '{}'.format(_status, response.status_code, response.get_data().decode()) + + data = response.get_data() + with suppress(UnicodeDecodeError): + data = data.decode() + if accept == JSON: + data = json.loads(data) if data else {} + return data, response + + def get(self, + uri: str, + query: Query = tuple(), + item: str = None, + status: Status = 200, + accept: str = JSON, + headers: dict = None, + **kw) -> Res: + """ + Performs a GET. + + See the parameters in :meth:`ereuse_utils.test.Client.open`. + Moreover: + + :param query: A dictionary of query params. If a parameter is a + dict or a list, it will be parsed to JSON, then + all params are encoded with ``urlencode``. + :param kw: Kwargs passed into parent ``open``. + """ + return super().get(uri, item=item, status=status, accept=accept, headers=headers, + query=query, **kw) + + def post(self, + uri: str, + data: str or dict, + query: Query = tuple(), + status: Status = 201, + content_type: str = JSON, + accept: str = JSON, + headers: dict = None, + **kw) -> Res: + """ + Performs a POST. + + See the parameters in :meth:`ereuse_utils.test.Client.open`. + """ + return super().post(uri, data=data, status=status, content_type=content_type, + accept=accept, headers=headers, query=query, **kw) + + def patch(self, + uri: str, + data: str or dict, + query: Query = tuple(), + status: Status = 200, + content_type: str = JSON, + item: str = None, + accept: str = JSON, + headers: dict = None, + **kw) -> Res: + """ + Performs a PATCH. + + See the parameters in :meth:`ereuse_utils.test.Client.open`. + """ + return super().patch(uri, item=item, data=data, status=status, content_type=content_type, + accept=accept, headers=headers, query=query, **kw) + + def put(self, + uri: str, + data: str or dict, + query: Query = tuple(), + status: Status = 201, + content_type: str = JSON, + item: str = None, + accept: str = JSON, + headers: dict = None, + **kw) -> Res: + return super().put(uri, item=item, data=data, status=status, content_type=content_type, + accept=accept, headers=headers, query=query, **kw) + + def delete(self, + uri: str, + query: Query = tuple(), + item: str = None, + status: Status = 204, + accept: str = JSON, + headers: dict = None, + **kw) -> Res: + return super().delete(uri, query=query, item=item, status=status, accept=accept, + headers=headers, **kw) diff --git a/ereuse_devicehub/ereuse_utils/text.py b/ereuse_devicehub/ereuse_utils/text.py new file mode 100644 index 00000000..747aea52 --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/text.py @@ -0,0 +1,72 @@ +import ast +import re +from typing import Iterator, Set, Union + + +def grep(text: str, value: str): + """An easy 'grep -i' that yields lines where value is found.""" + for line in text.splitlines(): + if value in line: + yield line + + +def between(text: str, begin='(', end=')'): + """Dead easy text between two characters. + Not recursive or repetitions. + """ + return text.split(begin)[-1].split(end)[0] + + +def numbers(text: str) -> Iterator[Union[int, float]]: + """Gets numbers in strings with other characters. + + Integer Numbers: 1 2 3 987 +4 -8 + Decimal Numbers: 0.1 2. .3 .987 +4.0 -0.8 + Scientific Notation: 1e2 0.2e2 3.e2 .987e2 +4e-1 -8.e+2 + Numbers with percentages: 49% 32.39% + + This returns int or float. + """ + # From https://regexr.com/33jqd + for x in re.finditer(r'[+-]?(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?', text): + yield ast.literal_eval(x.group()) + + +def positive_percentages( + text: str, lengths: Set[int] = None, decimal_numbers: int = None +) -> Iterator[Union[int, float]]: + """Gets numbers postfixed with a '%' in strings with other characters. + + 1)100% 2)56.78% 3)56 78.90% 4)34.6789% some text + + :param text: The text to search for. + :param lengths: A set of lengths that the percentage + number should have to be considered valid. + Ex. {5,6} would validate '90.32' and '100.00' + """ + # From https://regexr.com/3aumh + for x in re.finditer(r'[\d|\.]+%', text): + num = x.group()[:-1] + if lengths: + if not len(num) in lengths: + continue + if decimal_numbers: + try: + pos = num.rindex('.') + except ValueError: + continue + else: + if len(num) - pos - 1 != decimal_numbers: + continue + yield float(num) + + +def macs(text: str) -> Iterator[str]: + """Find MACs in strings with other characters.""" + for x in re.finditer('{0}:{0}:{0}:{0}:{0}:{0}'.format(r'[a-fA-F0-9.+_-]+'), text): + yield x.group() + + +def clean(text: str) -> str: + """Trims the text and replaces multiple spaces with a single space.""" + return ' '.join(text.split()) diff --git a/ereuse_devicehub/ereuse_utils/usb_flash_drive.py b/ereuse_devicehub/ereuse_utils/usb_flash_drive.py new file mode 100644 index 00000000..6d878b0b --- /dev/null +++ b/ereuse_devicehub/ereuse_utils/usb_flash_drive.py @@ -0,0 +1,80 @@ +import usb.core +import usb.util +from usb import CLASS_MASS_STORAGE + +from ereuse_devicehub.ereuse_utils.naming import Naming + + +def plugged_usbs(multiple=True) -> map or dict: + """ + Gets the plugged-in USB Flash drives (pen-drives). + + If multiple is true, it returns a map, and a dict otherwise. + + If multiple is false, this method will raise a :class:`.NoUSBFound` if no USB is found. + """ + + class FindPenDrives(object): + # From https://github.com/pyusb/pyusb/blob/master/docs/tutorial.rst + def __init__(self, class_): + self._class = class_ + + def __call__(self, device): + # first, let's check the device + if device.bDeviceClass == self._class: + return True + # ok, transverse all devices to find an + # interface that matches our class + for cfg in device: + # find_descriptor: what's it? + intf = usb.util.find_descriptor(cfg, bInterfaceClass=self._class) + # We don't want Card readers + if intf is not None: + try: + product = intf.device.product.lower() + except ValueError as e: + if 'langid' in str(e): + raise OSError( + 'Cannot get "langid". Do you have permissions?' + ) + else: + raise e + if 'crw' not in product and 'reader' not in product: + return True + return False + + def get_pendrive(pen: usb.Device) -> dict: + if not pen.manufacturer or not pen.product or not pen.serial_number: + raise UsbDoesNotHaveHid() + manufacturer = pen.manufacturer.strip() or str(pen.idVendor) + model = pen.product.strip() or str(pen.idProduct) + serial_number = pen.serial_number.strip() + hid = Naming.hid('USBFlashDrive', manufacturer, model, serial_number) + return { + 'id': hid, # Make live easier to DeviceHubClient by using _id + 'hid': hid, + 'type': 'USBFlashDrive', + 'serialNumber': serial_number, + 'model': model, + 'manufacturer': manufacturer, + 'vendorId': pen.idVendor, + 'productId': pen.idProduct, + } + + result = usb.core.find( + find_all=multiple, custom_match=FindPenDrives(CLASS_MASS_STORAGE) + ) + if multiple: + return map(get_pendrive, result) + else: + if not result: + raise NoUSBFound() + return get_pendrive(result) + + +class NoUSBFound(Exception): + pass + + +class UsbDoesNotHaveHid(Exception): + pass diff --git a/ereuse_devicehub/migrations/script.py.mako b/ereuse_devicehub/migrations/script.py.mako index 3fbbfa7f..cabe65db 100644 --- a/ereuse_devicehub/migrations/script.py.mako +++ b/ereuse_devicehub/migrations/script.py.mako @@ -9,7 +9,7 @@ from alembic import op import sqlalchemy as sa import sqlalchemy_utils import citext -import teal +from ereuse_devicehub import teal ${imports if imports else ""} # revision identifiers, used by Alembic. diff --git a/ereuse_devicehub/migrations/versions/0cbd839b09ef_change_testdatastorage_smallint_for_.py b/ereuse_devicehub/migrations/versions/0cbd839b09ef_change_testdatastorage_smallint_for_.py index 12346873..3207c4a1 100644 --- a/ereuse_devicehub/migrations/versions/0cbd839b09ef_change_testdatastorage_smallint_for_.py +++ b/ereuse_devicehub/migrations/versions/0cbd839b09ef_change_testdatastorage_smallint_for_.py @@ -10,7 +10,7 @@ from alembic import op import sqlalchemy as sa import sqlalchemy_utils import citext -import teal +from ereuse_devicehub import teal # revision identifiers, used by Alembic. @@ -26,11 +26,32 @@ def get_inv(): raise ValueError("Inventory value is not specified") return INV + def upgrade(): - op.alter_column('test_data_storage', 'current_pending_sector_count', type_=sa.Integer(), schema=f'{get_inv()}') - op.alter_column('test_data_storage', 'offline_uncorrectable', type_=sa.Integer(), schema=f'{get_inv()}') + op.alter_column( + 'test_data_storage', + 'current_pending_sector_count', + type_=sa.Integer(), + schema=f'{get_inv()}', + ) + op.alter_column( + 'test_data_storage', + 'offline_uncorrectable', + type_=sa.Integer(), + schema=f'{get_inv()}', + ) def downgrade(): - op.alter_column('test_data_storage', 'current_pending_sector_count', type_=sa.SmallInteger(), schema=f'{get_inv()}') - op.alter_column('test_data_storage', 'offline_uncorrectable', type_=sa.SmallInteger(), schema=f'{get_inv()}') + op.alter_column( + 'test_data_storage', + 'current_pending_sector_count', + type_=sa.SmallInteger(), + schema=f'{get_inv()}', + ) + op.alter_column( + 'test_data_storage', + 'offline_uncorrectable', + type_=sa.SmallInteger(), + schema=f'{get_inv()}', + ) diff --git a/ereuse_devicehub/migrations/versions/21afd375a654_session_table.py b/ereuse_devicehub/migrations/versions/21afd375a654_session_table.py index 97a50435..644dfb1d 100644 --- a/ereuse_devicehub/migrations/versions/21afd375a654_session_table.py +++ b/ereuse_devicehub/migrations/versions/21afd375a654_session_table.py @@ -11,7 +11,7 @@ from sqlalchemy.dialects import postgresql import sqlalchemy as sa import sqlalchemy_utils import citext -import teal +from ereuse_devicehub import teal from ereuse_devicehub.resources.enums import SessionType diff --git a/ereuse_devicehub/migrations/versions/378b6b147b46_nullable.py b/ereuse_devicehub/migrations/versions/378b6b147b46_nullable.py index 4bc48443..a941f6c0 100644 --- a/ereuse_devicehub/migrations/versions/378b6b147b46_nullable.py +++ b/ereuse_devicehub/migrations/versions/378b6b147b46_nullable.py @@ -5,12 +5,12 @@ Revises: bf600ca861a4 Create Date: 2020-12-16 11:45:13.339624 """ -from alembic import context -from alembic import op +import citext import sqlalchemy as sa import sqlalchemy_utils -import citext -import teal +from alembic import context +from alembic import op +from ereuse_devicehub import teal # revision identifiers, used by Alembic. diff --git a/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py b/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py index 19e65087..8a2e8cf9 100644 --- a/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py +++ b/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py @@ -5,15 +5,14 @@ Revises: 51439cf24be8 Create Date: 2021-06-15 14:38:59.931818 """ -import teal import citext import sqlalchemy as sa +from ereuse_devicehub import teal from alembic import op from alembic import context from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = '3a3601ac8224' down_revision = '51439cf24be8' @@ -27,108 +26,143 @@ def get_inv(): raise ValueError("Inventory value is not specified") return INV + def upgrade(): - op.create_table('trade_document', - sa.Column( - 'updated', - sa.TIMESTAMP(timezone=True), - server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n ' - ), - sa.Column( - 'created', - sa.TIMESTAMP(timezone=True), - server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='When Devicehub created this.' - ), - sa.Column( - 'id', - sa.BigInteger(), - nullable=False, - comment='The identifier of the device for this database. Used only\n internally for software; users should not use this.\n ' - ), - sa.Column( - 'date', - sa.DateTime(), - nullable=True, - comment='The date of document, some documents need to have one date\n ' - ), - sa.Column( - 'id_document', - citext.CIText(), - nullable=True, - comment='The id of one document like invoice so they can be linked.' - ), - sa.Column( - 'description', - citext.CIText(), - nullable=True, - comment='A description of document.' - ), - sa.Column( - 'owner_id', - postgresql.UUID(as_uuid=True), - nullable=False - ), - sa.Column( - 'lot_id', - postgresql.UUID(as_uuid=True), - nullable=False - ), - sa.Column( - 'file_name', - citext.CIText(), - nullable=True, - comment='This is the name of the file when user up the document.' - ), - sa.Column( - 'file_hash', - citext.CIText(), - nullable=True, - comment='This is the hash of the file produced from frontend.' - ), - sa.Column( - 'url', - citext.CIText(), - teal.db.URL(), - nullable=True, - comment='This is the url where resides the document.' - ), - sa.ForeignKeyConstraint(['lot_id'], [f'{get_inv()}.lot.id'],), - sa.ForeignKeyConstraint(['owner_id'], ['common.user.id'],), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' + op.create_table( + 'trade_document', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column( + 'id', + sa.BigInteger(), + nullable=False, + comment='The identifier of the device for this database. Used only\n internally for software; users should not use this.\n ', + ), + sa.Column( + 'date', + sa.DateTime(), + nullable=True, + comment='The date of document, some documents need to have one date\n ', + ), + sa.Column( + 'id_document', + citext.CIText(), + nullable=True, + comment='The id of one document like invoice so they can be linked.', + ), + sa.Column( + 'description', + citext.CIText(), + nullable=True, + comment='A description of document.', + ), + sa.Column('owner_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('lot_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column( + 'file_name', + citext.CIText(), + nullable=True, + comment='This is the name of the file when user up the document.', + ), + sa.Column( + 'file_hash', + citext.CIText(), + nullable=True, + comment='This is the hash of the file produced from frontend.', + ), + sa.Column( + 'url', + citext.CIText(), + teal.db.URL(), + nullable=True, + comment='This is the url where resides the document.', + ), + sa.ForeignKeyConstraint( + ['lot_id'], + [f'{get_inv()}.lot.id'], + ), + sa.ForeignKeyConstraint( + ['owner_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', ) # Action document table - op.create_table('action_trade_document', - sa.Column('document_id', sa.BigInteger(), nullable=False), - sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['action_id'], [f'{get_inv()}.action.id'], ), - sa.ForeignKeyConstraint(['document_id'], [f'{get_inv()}.trade_document.id'], ), - sa.PrimaryKeyConstraint('document_id', 'action_id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'action_trade_document', + sa.Column('document_id', sa.BigInteger(), nullable=False), + sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['action_id'], + [f'{get_inv()}.action.id'], + ), + sa.ForeignKeyConstraint( + ['document_id'], + [f'{get_inv()}.trade_document.id'], + ), + sa.PrimaryKeyConstraint('document_id', 'action_id'), + schema=f'{get_inv()}', + ) - op.create_index('document_id', 'trade_document', ['id'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') - op.create_index(op.f('ix_trade_document_created'), 'trade_document', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_trade_document_updated'), 'trade_document', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_index( + 'document_id', + 'trade_document', + ['id'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_trade_document_created'), + 'trade_document', + ['created'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_trade_document_updated'), + 'trade_document', + ['updated'], + unique=False, + schema=f'{get_inv()}', + ) - op.create_table('confirm_document', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), + op.create_table( + 'confirm_document', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), + sa.ForeignKeyConstraint( + ['action_id'], + [f'{get_inv()}.action.id'], + ), + sa.ForeignKeyConstraint( + ['user_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.ForeignKeyConstraint(['action_id'], [f'{get_inv()}.action.id'], ), - sa.ForeignKeyConstraint(['user_id'], ['common.user.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) def downgrade(): op.drop_table('action_trade_document', schema=f'{get_inv()}') op.drop_table('confirm_document', schema=f'{get_inv()}') op.drop_table('trade_document', schema=f'{get_inv()}') - diff --git a/ereuse_devicehub/migrations/versions/4f33137586dd_sanitization.py b/ereuse_devicehub/migrations/versions/4f33137586dd_sanitization.py index 51fe4d61..7d3cb453 100644 --- a/ereuse_devicehub/migrations/versions/4f33137586dd_sanitization.py +++ b/ereuse_devicehub/migrations/versions/4f33137586dd_sanitization.py @@ -7,7 +7,7 @@ Create Date: 2023-02-13 18:01:00.092527 """ import citext import sqlalchemy as sa -import teal +from ereuse_devicehub import teal from alembic import context, op from sqlalchemy.dialects import postgresql diff --git a/ereuse_devicehub/migrations/versions/7ecb8ff7abad_documents.py b/ereuse_devicehub/migrations/versions/7ecb8ff7abad_documents.py index 83daaf61..5e2c00cf 100644 --- a/ereuse_devicehub/migrations/versions/7ecb8ff7abad_documents.py +++ b/ereuse_devicehub/migrations/versions/7ecb8ff7abad_documents.py @@ -9,7 +9,7 @@ from alembic import op import sqlalchemy as sa import sqlalchemy_utils import citext -import teal +from ereuse_devicehub import teal from alembic import op from alembic import context @@ -32,51 +32,98 @@ def get_inv(): def upgrade(): # Document table - op.create_table('document', - sa.Column('id', sa.BigInteger(), nullable=False), - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Document recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Document created this.'), - sa.Column('document_type', sa.Unicode(), nullable=False), - sa.Column('date', sa.TIMESTAMP(timezone=True), nullable=True), - sa.Column('id_document', sa.Unicode(), nullable=True), - sa.Column('owner_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('file_name', sa.Unicode(), nullable=False), - sa.Column('file_hash', sa.Unicode(), nullable=False), - sa.Column('url', sa.Unicode(), nullable=True), - - sa.ForeignKeyConstraint(['owner_id'], ['common.user.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) - op.create_index('generic_document_id', 'document', ['id'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') - op.create_index(op.f('ix_document_created'), 'document', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_document_updated'), 'document', ['updated'], unique=False, schema=f'{get_inv()}') - op.create_index('document_type_index', 'document', ['document_type'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') - + op.create_table( + 'document', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Document recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Document created this.', + ), + sa.Column('document_type', sa.Unicode(), nullable=False), + sa.Column('date', sa.TIMESTAMP(timezone=True), nullable=True), + sa.Column('id_document', sa.Unicode(), nullable=True), + sa.Column('owner_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('file_name', sa.Unicode(), nullable=False), + sa.Column('file_hash', sa.Unicode(), nullable=False), + sa.Column('url', sa.Unicode(), nullable=True), + sa.ForeignKeyConstraint( + ['owner_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) + op.create_index( + 'generic_document_id', + 'document', + ['id'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_document_created'), + 'document', + ['created'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_document_updated'), + 'document', + ['updated'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + 'document_type_index', + 'document', + ['document_type'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) # DataWipeDocument table - op.create_table('data_wipe_document', - sa.Column('id', sa.BigInteger(), nullable=False), - sa.Column('software', sa.Unicode(), nullable=True), - sa.Column('success', sa.Boolean(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.document.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) - + op.create_table( + 'data_wipe_document', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('software', sa.Unicode(), nullable=True), + sa.Column('success', sa.Boolean(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.document.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # DataWipe table - op.create_table('data_wipe', - sa.Column('document_id', sa.BigInteger(), nullable=False), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['document_id'], [f'{get_inv()}.document.id'], ), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'data_wipe', + sa.Column('document_id', sa.BigInteger(), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['document_id'], + [f'{get_inv()}.document.id'], + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) def downgrade(): diff --git a/ereuse_devicehub/migrations/versions/968b79fa7756_upgrade_confirmrevoke.py b/ereuse_devicehub/migrations/versions/968b79fa7756_upgrade_confirmrevoke.py index bd3a995d..cb066b1a 100644 --- a/ereuse_devicehub/migrations/versions/968b79fa7756_upgrade_confirmrevoke.py +++ b/ereuse_devicehub/migrations/versions/968b79fa7756_upgrade_confirmrevoke.py @@ -10,7 +10,7 @@ from alembic import context import sqlalchemy as sa import sqlalchemy_utils import citext -import teal +from ereuse_devicehub import teal # revision identifiers, used by Alembic. @@ -26,10 +26,10 @@ def get_inv(): raise ValueError("Inventory value is not specified") return INV + def upgrade(): con = op.get_bind() - confirmsRevokes_sql = f"select * from {get_inv()}.action as action join {get_inv()}.confirm as confirm on action.id=confirm.id where action.type='ConfirmRevoke'" revokes_sql = f"select confirm.id, confirm.action_id from {get_inv()}.action as action join {get_inv()}.confirm as confirm on action.id=confirm.id where action.type='Revoke'" confirmsRevokes = [a for a in con.execute(confirmsRevokes_sql)] @@ -40,12 +40,12 @@ def upgrade(): revoke_id = ac.action_id trade_id = revokes[revoke_id] sql_action = f"update {get_inv()}.action set type='Revoke' where id='{ac_id}'" - sql_confirm = f"update {get_inv()}.confirm set action_id='{trade_id}' where id='{ac_id}'" + sql_confirm = ( + f"update {get_inv()}.confirm set action_id='{trade_id}' where id='{ac_id}'" + ) con.execute(sql_action) con.execute(sql_confirm) - - def downgrade(): pass diff --git a/ereuse_devicehub/migrations/versions/b4bd1538bad5_update_live.py b/ereuse_devicehub/migrations/versions/b4bd1538bad5_update_live.py index f340df6a..d54cb16f 100644 --- a/ereuse_devicehub/migrations/versions/b4bd1538bad5_update_live.py +++ b/ereuse_devicehub/migrations/versions/b4bd1538bad5_update_live.py @@ -6,7 +6,7 @@ Create Date: 2020-12-29 20:19:46.981207 """ import sqlalchemy as sa -import teal +from ereuse_devicehub import teal from alembic import context, op from sqlalchemy.dialects import postgresql diff --git a/ereuse_devicehub/migrations/versions/bf600ca861a4_adding_hid.py b/ereuse_devicehub/migrations/versions/bf600ca861a4_adding_hid.py index ae68e3ed..76f2c0f2 100644 --- a/ereuse_devicehub/migrations/versions/bf600ca861a4_adding_hid.py +++ b/ereuse_devicehub/migrations/versions/bf600ca861a4_adding_hid.py @@ -10,7 +10,7 @@ from alembic import op import sqlalchemy as sa import sqlalchemy_utils import citext -import teal +from ereuse_devicehub import teal # revision identifiers, used by Alembic. @@ -26,6 +26,7 @@ def get_inv(): raise ValueError("Inventory value is not specified") return INV + def upgrade(): con = op.get_bind() sql = f""" @@ -60,6 +61,5 @@ def upgrade(): con.execute(sql) - def downgrade(): pass diff --git a/ereuse_devicehub/migrations/versions/e93aec8fc41f_added_assigned_action.py b/ereuse_devicehub/migrations/versions/e93aec8fc41f_added_assigned_action.py index b335256d..f6b97f88 100644 --- a/ereuse_devicehub/migrations/versions/e93aec8fc41f_added_assigned_action.py +++ b/ereuse_devicehub/migrations/versions/e93aec8fc41f_added_assigned_action.py @@ -10,7 +10,7 @@ import sqlalchemy as sa from alembic import context import sqlalchemy_utils import citext -import teal +from ereuse_devicehub import teal from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. @@ -26,48 +26,85 @@ def get_inv(): raise ValueError("Inventory value is not specified") return INV + def upgrade(): # Allocate action op.drop_table('allocate', schema=f'{get_inv()}') - op.create_table('allocate', - sa.Column('final_user_code', citext.CIText(), default='', nullable=True, - comment = "This is a internal code for mainteing the secrets of the personal datas of the new holder"), - sa.Column('transaction', citext.CIText(), nullable=True, comment='The code used from the owner for relation with external tool.'), + op.create_table( + 'allocate', + sa.Column( + 'final_user_code', + citext.CIText(), + default='', + nullable=True, + comment="This is a internal code for mainteing the secrets of the personal datas of the new holder", + ), + sa.Column( + 'transaction', + citext.CIText(), + nullable=True, + comment='The code used from the owner for relation with external tool.', + ), sa.Column('end_users', sa.Numeric(precision=4), nullable=True), sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' + schema=f'{get_inv()}', ) # Deallocate action op.drop_table('deallocate', schema=f'{get_inv()}') - op.create_table('deallocate', - sa.Column('transaction', citext.CIText(), nullable=True, comment='The code used from the owner for relation with external tool.'), + op.create_table( + 'deallocate', + sa.Column( + 'transaction', + citext.CIText(), + nullable=True, + comment='The code used from the owner for relation with external tool.', + ), sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' + schema=f'{get_inv()}', ) # Add allocate as a column in device - op.add_column('device', sa.Column('allocated', sa.Boolean(), nullable=True), schema=f'{get_inv()}') + op.add_column( + 'device', + sa.Column('allocated', sa.Boolean(), nullable=True), + schema=f'{get_inv()}', + ) # Receive action op.drop_table('receive', schema=f'{get_inv()}') # Live action op.drop_table('live', schema=f'{get_inv()}') - op.create_table('live', + op.create_table( + 'live', sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('serial_number', sa.Unicode(), nullable=True, - comment='The serial number of the Hard Disk in lower case.'), + sa.Column( + 'serial_number', + sa.Unicode(), + nullable=True, + comment='The serial number of the Hard Disk in lower case.', + ), sa.Column('usage_time_hdd', sa.Interval(), nullable=True), sa.Column('snapshot_uuid', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' + schema=f'{get_inv()}', ) + def downgrade(): op.drop_table('allocate', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/migrations/versions/fbb7e2a0cde0_initial.py b/ereuse_devicehub/migrations/versions/fbb7e2a0cde0_initial.py index b6ad7490..08758ec3 100644 --- a/ereuse_devicehub/migrations/versions/fbb7e2a0cde0_initial.py +++ b/ereuse_devicehub/migrations/versions/fbb7e2a0cde0_initial.py @@ -8,7 +8,7 @@ Create Date: 2020-05-07 10:04:40.269511 import citext import sqlalchemy as sa import sqlalchemy_utils -import teal +from ereuse_devicehub import teal from alembic import context from alembic import op from sqlalchemy.dialects import postgresql @@ -35,1565 +35,6789 @@ def upgrade(): op.execute(f"create schema {get_inv()}") # Inventory table - op.create_table('inventory', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', sa.Unicode(), nullable=False, - comment='The name of the inventory as in the URL and schema.'), - sa.Column('name', citext.CIText(), nullable=False, comment='The human name of the inventory.'), - sa.Column('tag_provider', teal.db.URL(), nullable=False), - sa.Column('tag_token', postgresql.UUID(as_uuid=True), nullable=False, - comment='The token to access a Tag service.'), - sa.Column('org_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('name'), - sa.UniqueConstraint('tag_token'), - schema='common' - ) - op.create_index('id_hash', 'inventory', ['id'], unique=False, schema='common', postgresql_using='hash') - op.create_index(op.f('ix_common_inventory_created'), 'inventory', ['created'], unique=False, schema='common') - op.create_index(op.f('ix_common_inventory_updated'), 'inventory', ['updated'], unique=False, schema='common') + op.create_table( + 'inventory', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column( + 'id', + sa.Unicode(), + nullable=False, + comment='The name of the inventory as in the URL and schema.', + ), + sa.Column( + 'name', + citext.CIText(), + nullable=False, + comment='The human name of the inventory.', + ), + sa.Column('tag_provider', teal.db.URL(), nullable=False), + sa.Column( + 'tag_token', + postgresql.UUID(as_uuid=True), + nullable=False, + comment='The token to access a Tag service.', + ), + sa.Column('org_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('name'), + sa.UniqueConstraint('tag_token'), + schema='common', + ) + op.create_index( + 'id_hash', + 'inventory', + ['id'], + unique=False, + schema='common', + postgresql_using='hash', + ) + op.create_index( + op.f('ix_common_inventory_created'), + 'inventory', + ['created'], + unique=False, + schema='common', + ) + op.create_index( + op.f('ix_common_inventory_updated'), + 'inventory', + ['updated'], + unique=False, + schema='common', + ) # Manufacturer table - op.create_table('manufacturer', - sa.Column('name', citext.CIText(), nullable=False, - comment='The normalized name of the manufacturer.'), - sa.Column('url', teal.db.URL(), nullable=True, - comment='An URL to a page describing the manufacturer.'), - sa.Column('logo', teal.db.URL(), nullable=True, - comment='An URL pointing to the logo of the manufacturer.'), - sa.PrimaryKeyConstraint('name'), - sa.UniqueConstraint('url'), - schema='common' - ) + op.create_table( + 'manufacturer', + sa.Column( + 'name', + citext.CIText(), + nullable=False, + comment='The normalized name of the manufacturer.', + ), + sa.Column( + 'url', + teal.db.URL(), + nullable=True, + comment='An URL to a page describing the manufacturer.', + ), + sa.Column( + 'logo', + teal.db.URL(), + nullable=True, + comment='An URL pointing to the logo of the manufacturer.', + ), + sa.PrimaryKeyConstraint('name'), + sa.UniqueConstraint('url'), + schema='common', + ) # User table - op.create_table('user', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('email', sqlalchemy_utils.types.email.EmailType(length=255), nullable=False), - sa.Column('password', sqlalchemy_utils.types.password.PasswordType(max_length=64), nullable=True), - sa.Column('token', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('ethereum_address', citext.CIText(), nullable=True), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('email'), - sa.UniqueConstraint('ethereum_address'), - sa.UniqueConstraint('token'), - schema='common' - ) - op.create_index(op.f('ix_common_user_created'), 'user', ['created'], unique=False, schema='common') - op.create_index(op.f('ix_common_user_updated'), 'user', ['updated'], unique=False, schema='common') + op.create_table( + 'user', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column( + 'email', sqlalchemy_utils.types.email.EmailType(length=255), nullable=False + ), + sa.Column( + 'password', + sqlalchemy_utils.types.password.PasswordType(max_length=64), + nullable=True, + ), + sa.Column('token', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('ethereum_address', citext.CIText(), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email'), + sa.UniqueConstraint('ethereum_address'), + sa.UniqueConstraint('token'), + schema='common', + ) + op.create_index( + op.f('ix_common_user_created'), + 'user', + ['created'], + unique=False, + schema='common', + ) + op.create_index( + op.f('ix_common_user_updated'), + 'user', + ['updated'], + unique=False, + schema='common', + ) # User Inventory table - op.create_table('user_inventory', - sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('inventory_id', sa.Unicode(), nullable=False), - sa.ForeignKeyConstraint(['inventory_id'], ['common.inventory.id'], ), - sa.ForeignKeyConstraint(['user_id'], ['common.user.id'], ), - sa.PrimaryKeyConstraint('user_id', 'inventory_id'), - schema='common' - ) + op.create_table( + 'user_inventory', + sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('inventory_id', sa.Unicode(), nullable=False), + sa.ForeignKeyConstraint( + ['inventory_id'], + ['common.inventory.id'], + ), + sa.ForeignKeyConstraint( + ['user_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('user_id', 'inventory_id'), + schema='common', + ) # Device table - op.create_table('device', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', sa.BigInteger(), nullable=False, - comment='The identifier of the device for this database. Used only\n internally for software; users should not use this.\n '), - sa.Column('type', sa.Unicode(length=32), nullable=False), - sa.Column('hid', sa.Unicode(), nullable=True, - comment='The Hardware ID (HID) is the unique ID traceability\n systems use to ID a device globally. This field is auto-generated\n from Devicehub using literal identifiers from the device,\n so it can re-generated *offline*.\n \n The HID is the result of concatenating,\n in the following order: the type of device (ex. Computer),\n the manufacturer name, the model name, and the S/N. It is joined\n with hyphens, and adapted to comply with the URI specification, so\n it can be used in the URI identifying the device on the Internet.\n The conversion is done as follows:\n \n 1. non-ASCII characters are converted to their ASCII equivalent or\n removed.\n 2. Characterst that are not letters or numbers are converted to \n underscores, in a way that there are no trailing underscores\n and no underscores together, and they are set to lowercase.\n \n Ex. ``laptop-acer-aod270-lusga_0d0242201212c7614``\n '), - sa.Column('model', sa.Unicode(), nullable=True, - comment='The model of the device in lower case.\n\n\n The model is the unambiguous, as technical as possible, denomination\n for the product. This field, among others, is used to identify\n the product.\n '), - sa.Column('manufacturer', sa.Unicode(), nullable=True, - comment='The normalized name of the manufacturer,\n in lower case.\n\n Although as of now Devicehub does not enforce normalization,\n users can choose a list of normalized manufacturer names\n from the own ``/manufacturers`` REST endpoint.\n '), - sa.Column('serial_number', sa.Unicode(), nullable=True, - comment='The serial number of the device in lower case.'), - sa.Column('brand', citext.CIText(), nullable=True, - comment='A naming for consumers. This field can represent\n several models, so it can be ambiguous, and it is not used to\n identify the product.\n '), - sa.Column('generation', sa.SmallInteger(), nullable=True, comment='The generation of the device.'), - sa.Column('version', citext.CIText(), nullable=True, - comment='The version code of this device, like v1 or A001.'), - sa.Column('weight', sa.Float(decimal_return_scale=4), nullable=True, - comment='The weight of the device in Kg.'), - sa.Column('width', sa.Float(decimal_return_scale=4), nullable=True, - comment='The width of the device in meters.'), - sa.Column('height', sa.Float(decimal_return_scale=4), nullable=True, - comment='The height of the device in meters.'), - sa.Column('depth', sa.Float(decimal_return_scale=4), nullable=True, - comment='The depth of the device in meters.'), - sa.Column('color', sqlalchemy_utils.types.color.ColorType(length=20), nullable=True, - comment='The predominant color of the device.'), - sa.Column('production_date', sa.DateTime(), nullable=True, - comment='The date of production of the device.\n This is timezone naive, as Workbench cannot report this data with timezone information.\n '), - sa.Column('variant', citext.CIText(), nullable=True, - comment='A variant or sub-model of the device.'), - sa.Column('sku', citext.CIText(), nullable=True, - comment='The Stock Keeping Unit (SKU), i.e. a\n merchant-specific identifier for a product or service.\n '), - sa.Column('image', teal.db.URL(), nullable=True, comment='An image of the device.'), - sa.Column('max_drill_bit_size', sa.SmallInteger(), nullable=True), - sa.Column('size', sa.SmallInteger(), nullable=True, comment='The capacity in Liters.'), - sa.Column('max_allowed_weight', sa.Integer(), nullable=True), - sa.Column('wheel_size', sa.SmallInteger(), nullable=True), - sa.Column('gears', sa.SmallInteger(), nullable=True), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) - op.create_index('device_id', 'device', ['id'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') - op.create_index(op.f('ix_device_created'), 'device', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_device_updated'), 'device', ['updated'], unique=False, schema=f'{get_inv()}') - op.create_index('type_index', 'device', ['type'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') - op.create_table('agent', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('type', sa.Unicode(), nullable=False), - sa.Column('name', citext.CIText(), nullable=True, - comment='The name of the organization or person.'), - sa.Column('tax_id', sa.Unicode(length=32), nullable=True, - comment='The Tax / Fiscal ID of the organization, \n e.g. the TIN in the US or the CIF/NIF in Spain.\n '), - sa.Column('country', - sa.Enum('AF', 'AX', 'AL', 'DZ', 'AS', 'AD', 'AO', 'AI', 'AQ', 'AG', 'AR', 'AM', 'AW', - 'AU', 'AT', 'AZ', 'BS', 'BH', 'BD', 'BB', 'BY', 'BE', 'BZ', 'BJ', 'BM', 'BT', - 'BO', 'BQ', 'BA', 'BW', 'BV', 'BR', 'IO', 'BN', 'BG', 'BF', 'BI', 'KH', 'CM', - 'CA', 'CV', 'KY', 'CF', 'TD', 'CL', 'CN', 'CX', 'CC', 'CO', 'KM', 'CG', 'CD', - 'CK', 'CR', 'CI', 'HR', 'CU', 'CW', 'CY', 'CZ', 'DK', 'DJ', 'DM', 'DO', 'EC', - 'EG', 'SV', 'GQ', 'ER', 'EE', 'ET', 'FK', 'FO', 'FJ', 'FI', 'FR', 'GF', 'PF', - 'TF', 'GA', 'GM', 'GE', 'DE', 'GH', 'GI', 'GR', 'GL', 'GD', 'GP', 'GU', 'GT', - 'GG', 'GN', 'GW', 'GY', 'HT', 'HM', 'VA', 'HN', 'HK', 'HU', 'IS', 'IN', 'ID', - 'IR', 'IQ', 'IE', 'IM', 'IL', 'IT', 'JM', 'JP', 'JE', 'JO', 'KZ', 'KE', 'KI', - 'KP', 'KR', 'KW', 'KG', 'LA', 'LV', 'LB', 'LS', 'LR', 'LY', 'LI', 'LT', 'LU', - 'MO', 'MK', 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MQ', 'MR', 'MU', 'YT', - 'MX', 'FM', 'MD', 'MC', 'MN', 'ME', 'MS', 'MA', 'MZ', 'MM', 'NA', 'NR', 'NP', - 'NL', 'NC', 'NZ', 'NI', 'NE', 'NG', 'NU', 'NF', 'MP', 'NO', 'OM', 'PK', 'PW', - 'PS', 'PA', 'PG', 'PY', 'PE', 'PH', 'PN', 'PL', 'PT', 'PR', 'QA', 'RE', 'RO', - 'RU', 'RW', 'BL', 'SH', 'KN', 'LC', 'MF', 'PM', 'VC', 'WS', 'SM', 'ST', 'SA', - 'SN', 'RS', 'SC', 'SL', 'SG', 'SX', 'SK', 'SI', 'SB', 'SO', 'ZA', 'GS', 'SS', - 'ES', 'LK', 'SD', 'SR', 'SJ', 'SZ', 'SE', 'CH', 'SY', 'TW', 'TJ', 'TZ', 'TH', - 'TL', 'TG', 'TK', 'TO', 'TT', 'TN', 'TR', 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', - 'GB', 'US', 'UM', 'UY', 'UZ', 'VU', 'VE', 'VN', 'VG', 'VI', 'WF', 'EH', 'YE', - 'ZM', 'ZW', name='country'), nullable=True, - comment='Country issuing the tax_id number.'), - sa.Column('telephone', sqlalchemy_utils.types.phone_number.PhoneNumberType(length=20), - nullable=True), - sa.Column('email', sqlalchemy_utils.types.email.EmailType(length=255), nullable=True), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('email'), - sa.UniqueConstraint('tax_id', 'country', name='Registration Number per country.'), - sa.UniqueConstraint('tax_id', 'name', name='One tax ID with one name.'), - schema=f'{get_inv()}' - ) - op.create_index('agent_type', 'agent', ['type'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') - op.create_index(op.f('ix_agent_created'), 'agent', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_agent_updated'), 'agent', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_table( + 'device', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column( + 'id', + sa.BigInteger(), + nullable=False, + comment='The identifier of the device for this database. Used only\n internally for software; users should not use this.\n ', + ), + sa.Column('type', sa.Unicode(length=32), nullable=False), + sa.Column( + 'hid', + sa.Unicode(), + nullable=True, + comment='The Hardware ID (HID) is the unique ID traceability\n systems use to ID a device globally. This field is auto-generated\n from Devicehub using literal identifiers from the device,\n so it can re-generated *offline*.\n \n The HID is the result of concatenating,\n in the following order: the type of device (ex. Computer),\n the manufacturer name, the model name, and the S/N. It is joined\n with hyphens, and adapted to comply with the URI specification, so\n it can be used in the URI identifying the device on the Internet.\n The conversion is done as follows:\n \n 1. non-ASCII characters are converted to their ASCII equivalent or\n removed.\n 2. Characterst that are not letters or numbers are converted to \n underscores, in a way that there are no trailing underscores\n and no underscores together, and they are set to lowercase.\n \n Ex. ``laptop-acer-aod270-lusga_0d0242201212c7614``\n ', + ), + sa.Column( + 'model', + sa.Unicode(), + nullable=True, + comment='The model of the device in lower case.\n\n\n The model is the unambiguous, as technical as possible, denomination\n for the product. This field, among others, is used to identify\n the product.\n ', + ), + sa.Column( + 'manufacturer', + sa.Unicode(), + nullable=True, + comment='The normalized name of the manufacturer,\n in lower case.\n\n Although as of now Devicehub does not enforce normalization,\n users can choose a list of normalized manufacturer names\n from the own ``/manufacturers`` REST endpoint.\n ', + ), + sa.Column( + 'serial_number', + sa.Unicode(), + nullable=True, + comment='The serial number of the device in lower case.', + ), + sa.Column( + 'brand', + citext.CIText(), + nullable=True, + comment='A naming for consumers. This field can represent\n several models, so it can be ambiguous, and it is not used to\n identify the product.\n ', + ), + sa.Column( + 'generation', + sa.SmallInteger(), + nullable=True, + comment='The generation of the device.', + ), + sa.Column( + 'version', + citext.CIText(), + nullable=True, + comment='The version code of this device, like v1 or A001.', + ), + sa.Column( + 'weight', + sa.Float(decimal_return_scale=4), + nullable=True, + comment='The weight of the device in Kg.', + ), + sa.Column( + 'width', + sa.Float(decimal_return_scale=4), + nullable=True, + comment='The width of the device in meters.', + ), + sa.Column( + 'height', + sa.Float(decimal_return_scale=4), + nullable=True, + comment='The height of the device in meters.', + ), + sa.Column( + 'depth', + sa.Float(decimal_return_scale=4), + nullable=True, + comment='The depth of the device in meters.', + ), + sa.Column( + 'color', + sqlalchemy_utils.types.color.ColorType(length=20), + nullable=True, + comment='The predominant color of the device.', + ), + sa.Column( + 'production_date', + sa.DateTime(), + nullable=True, + comment='The date of production of the device.\n This is timezone naive, as Workbench cannot report this data with timezone information.\n ', + ), + sa.Column( + 'variant', + citext.CIText(), + nullable=True, + comment='A variant or sub-model of the device.', + ), + sa.Column( + 'sku', + citext.CIText(), + nullable=True, + comment='The Stock Keeping Unit (SKU), i.e. a\n merchant-specific identifier for a product or service.\n ', + ), + sa.Column( + 'image', teal.db.URL(), nullable=True, comment='An image of the device.' + ), + sa.Column('max_drill_bit_size', sa.SmallInteger(), nullable=True), + sa.Column( + 'size', sa.SmallInteger(), nullable=True, comment='The capacity in Liters.' + ), + sa.Column('max_allowed_weight', sa.Integer(), nullable=True), + sa.Column('wheel_size', sa.SmallInteger(), nullable=True), + sa.Column('gears', sa.SmallInteger(), nullable=True), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) + op.create_index( + 'device_id', + 'device', + ['id'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_device_created'), + 'device', + ['created'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_device_updated'), + 'device', + ['updated'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + 'type_index', + 'device', + ['type'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) + op.create_table( + 'agent', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('type', sa.Unicode(), nullable=False), + sa.Column( + 'name', + citext.CIText(), + nullable=True, + comment='The name of the organization or person.', + ), + sa.Column( + 'tax_id', + sa.Unicode(length=32), + nullable=True, + comment='The Tax / Fiscal ID of the organization, \n e.g. the TIN in the US or the CIF/NIF in Spain.\n ', + ), + sa.Column( + 'country', + sa.Enum( + 'AF', + 'AX', + 'AL', + 'DZ', + 'AS', + 'AD', + 'AO', + 'AI', + 'AQ', + 'AG', + 'AR', + 'AM', + 'AW', + 'AU', + 'AT', + 'AZ', + 'BS', + 'BH', + 'BD', + 'BB', + 'BY', + 'BE', + 'BZ', + 'BJ', + 'BM', + 'BT', + 'BO', + 'BQ', + 'BA', + 'BW', + 'BV', + 'BR', + 'IO', + 'BN', + 'BG', + 'BF', + 'BI', + 'KH', + 'CM', + 'CA', + 'CV', + 'KY', + 'CF', + 'TD', + 'CL', + 'CN', + 'CX', + 'CC', + 'CO', + 'KM', + 'CG', + 'CD', + 'CK', + 'CR', + 'CI', + 'HR', + 'CU', + 'CW', + 'CY', + 'CZ', + 'DK', + 'DJ', + 'DM', + 'DO', + 'EC', + 'EG', + 'SV', + 'GQ', + 'ER', + 'EE', + 'ET', + 'FK', + 'FO', + 'FJ', + 'FI', + 'FR', + 'GF', + 'PF', + 'TF', + 'GA', + 'GM', + 'GE', + 'DE', + 'GH', + 'GI', + 'GR', + 'GL', + 'GD', + 'GP', + 'GU', + 'GT', + 'GG', + 'GN', + 'GW', + 'GY', + 'HT', + 'HM', + 'VA', + 'HN', + 'HK', + 'HU', + 'IS', + 'IN', + 'ID', + 'IR', + 'IQ', + 'IE', + 'IM', + 'IL', + 'IT', + 'JM', + 'JP', + 'JE', + 'JO', + 'KZ', + 'KE', + 'KI', + 'KP', + 'KR', + 'KW', + 'KG', + 'LA', + 'LV', + 'LB', + 'LS', + 'LR', + 'LY', + 'LI', + 'LT', + 'LU', + 'MO', + 'MK', + 'MG', + 'MW', + 'MY', + 'MV', + 'ML', + 'MT', + 'MH', + 'MQ', + 'MR', + 'MU', + 'YT', + 'MX', + 'FM', + 'MD', + 'MC', + 'MN', + 'ME', + 'MS', + 'MA', + 'MZ', + 'MM', + 'NA', + 'NR', + 'NP', + 'NL', + 'NC', + 'NZ', + 'NI', + 'NE', + 'NG', + 'NU', + 'NF', + 'MP', + 'NO', + 'OM', + 'PK', + 'PW', + 'PS', + 'PA', + 'PG', + 'PY', + 'PE', + 'PH', + 'PN', + 'PL', + 'PT', + 'PR', + 'QA', + 'RE', + 'RO', + 'RU', + 'RW', + 'BL', + 'SH', + 'KN', + 'LC', + 'MF', + 'PM', + 'VC', + 'WS', + 'SM', + 'ST', + 'SA', + 'SN', + 'RS', + 'SC', + 'SL', + 'SG', + 'SX', + 'SK', + 'SI', + 'SB', + 'SO', + 'ZA', + 'GS', + 'SS', + 'ES', + 'LK', + 'SD', + 'SR', + 'SJ', + 'SZ', + 'SE', + 'CH', + 'SY', + 'TW', + 'TJ', + 'TZ', + 'TH', + 'TL', + 'TG', + 'TK', + 'TO', + 'TT', + 'TN', + 'TR', + 'TM', + 'TC', + 'TV', + 'UG', + 'UA', + 'AE', + 'GB', + 'US', + 'UM', + 'UY', + 'UZ', + 'VU', + 'VE', + 'VN', + 'VG', + 'VI', + 'WF', + 'EH', + 'YE', + 'ZM', + 'ZW', + name='country', + ), + nullable=True, + comment='Country issuing the tax_id number.', + ), + sa.Column( + 'telephone', + sqlalchemy_utils.types.phone_number.PhoneNumberType(length=20), + nullable=True, + ), + sa.Column( + 'email', sqlalchemy_utils.types.email.EmailType(length=255), nullable=True + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email'), + sa.UniqueConstraint( + 'tax_id', 'country', name='Registration Number per country.' + ), + sa.UniqueConstraint('tax_id', 'name', name='One tax ID with one name.'), + schema=f'{get_inv()}', + ) + op.create_index( + 'agent_type', + 'agent', + ['type'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_agent_created'), + 'agent', + ['created'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_agent_updated'), + 'agent', + ['updated'], + unique=False, + schema=f'{get_inv()}', + ) # Computer table - op.create_table('computer', - sa.Column('id', sa.BigInteger(), nullable=False), - sa.Column('chassis', - sa.Enum('Tower', 'Docking', 'AllInOne', 'Microtower', 'PizzaBox', 'Lunchbox', 'Stick', - 'Netbook', 'Handheld', 'Laptop', 'Convertible', 'Detachable', 'Tablet', 'Virtual', - name='computerchassis'), nullable=False, - comment='The physical form of the computer.\n\n It is a subset of the Linux definition of DMI / DMI decode.\n '), - sa.Column('ethereum_address', citext.CIText(), nullable=True), - sa.Column('deposit', sa.Integer(), nullable=True), - sa.Column('owner_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('transfer_state', teal.db.IntEnum(TransferState), nullable=False, - comment='State of transfer for a given Lot of devices.\n '), - sa.Column('receiver_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('deliverynote_address', citext.CIText(), nullable=True), - sa.Column('layout', - sa.Enum('US', 'AF', 'ARA', 'AL', 'AM', 'AT', 'AU', 'AZ', 'BY', 'BE', 'BD', 'BA', 'BR', - 'BG', 'DZ', 'MA', 'CM', 'MM', 'CA', 'CD', 'CN', 'HR', 'CZ', 'DK', 'NL', 'BT', - 'EE', 'IR', 'IQ', 'FO', 'FI', 'FR', 'GH', 'GN', 'GE', 'DE', 'GR', 'HU', 'IL', - 'IT', 'JP', 'KG', 'KH', 'KZ', 'LA', 'LATAM', 'LT', 'LV', 'MAO', 'ME', 'MK', 'MT', - 'MN', 'NO', 'PL', 'PT', 'RO', 'RU', 'RS', 'SI', 'SK', 'ES', 'SE', 'CH', 'SY', - 'TJ', 'LK', 'TH', 'TR', 'TW', 'UA', 'GB', 'UZ', 'VN', 'KR', 'IE', 'PK', 'MV', - 'ZA', 'EPO', 'NP', 'NG', 'ET', 'SN', 'BRAI', 'TM', 'ML', 'TZ', 'TG', 'KE', 'BW', - 'PH', 'MD', 'ID', 'MY', 'BN', 'IN', 'IS', 'NEC_VNDR_JP', name='layouts'), - nullable=True, - comment='Layout of a built-in keyboard of the computer,\n if any.\n '), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.device.id'], ), - sa.ForeignKeyConstraint(['owner_id'], ['common.user.id'], ), - sa.ForeignKeyConstraint(['receiver_id'], ['common.user.id'], ), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('ethereum_address'), - schema=f'{get_inv()}' - ) + op.create_table( + 'computer', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column( + 'chassis', + sa.Enum( + 'Tower', + 'Docking', + 'AllInOne', + 'Microtower', + 'PizzaBox', + 'Lunchbox', + 'Stick', + 'Netbook', + 'Handheld', + 'Laptop', + 'Convertible', + 'Detachable', + 'Tablet', + 'Virtual', + name='computerchassis', + ), + nullable=False, + comment='The physical form of the computer.\n\n It is a subset of the Linux definition of DMI / DMI decode.\n ', + ), + sa.Column('ethereum_address', citext.CIText(), nullable=True), + sa.Column('deposit', sa.Integer(), nullable=True), + sa.Column('owner_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column( + 'transfer_state', + teal.db.IntEnum(TransferState), + nullable=False, + comment='State of transfer for a given Lot of devices.\n ', + ), + sa.Column('receiver_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('deliverynote_address', citext.CIText(), nullable=True), + sa.Column( + 'layout', + sa.Enum( + 'US', + 'AF', + 'ARA', + 'AL', + 'AM', + 'AT', + 'AU', + 'AZ', + 'BY', + 'BE', + 'BD', + 'BA', + 'BR', + 'BG', + 'DZ', + 'MA', + 'CM', + 'MM', + 'CA', + 'CD', + 'CN', + 'HR', + 'CZ', + 'DK', + 'NL', + 'BT', + 'EE', + 'IR', + 'IQ', + 'FO', + 'FI', + 'FR', + 'GH', + 'GN', + 'GE', + 'DE', + 'GR', + 'HU', + 'IL', + 'IT', + 'JP', + 'KG', + 'KH', + 'KZ', + 'LA', + 'LATAM', + 'LT', + 'LV', + 'MAO', + 'ME', + 'MK', + 'MT', + 'MN', + 'NO', + 'PL', + 'PT', + 'RO', + 'RU', + 'RS', + 'SI', + 'SK', + 'ES', + 'SE', + 'CH', + 'SY', + 'TJ', + 'LK', + 'TH', + 'TR', + 'TW', + 'UA', + 'GB', + 'UZ', + 'VN', + 'KR', + 'IE', + 'PK', + 'MV', + 'ZA', + 'EPO', + 'NP', + 'NG', + 'ET', + 'SN', + 'BRAI', + 'TM', + 'ML', + 'TZ', + 'TG', + 'KE', + 'BW', + 'PH', + 'MD', + 'ID', + 'MY', + 'BN', + 'IN', + 'IS', + 'NEC_VNDR_JP', + name='layouts', + ), + nullable=True, + comment='Layout of a built-in keyboard of the computer,\n if any.\n ', + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.device.id'], + ), + sa.ForeignKeyConstraint( + ['owner_id'], + ['common.user.id'], + ), + sa.ForeignKeyConstraint( + ['receiver_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('ethereum_address'), + schema=f'{get_inv()}', + ) # Computer accessory - op.create_table('computer_accessory', - sa.Column('id', sa.BigInteger(), nullable=False), - sa.Column('layout', - sa.Enum('US', 'AF', 'ARA', 'AL', 'AM', 'AT', 'AU', 'AZ', 'BY', 'BE', 'BD', 'BA', 'BR', - 'BG', 'DZ', 'MA', 'CM', 'MM', 'CA', 'CD', 'CN', 'HR', 'CZ', 'DK', 'NL', 'BT', - 'EE', 'IR', 'IQ', 'FO', 'FI', 'FR', 'GH', 'GN', 'GE', 'DE', 'GR', 'HU', 'IL', - 'IT', 'JP', 'KG', 'KH', 'KZ', 'LA', 'LATAM', 'LT', 'LV', 'MAO', 'ME', 'MK', 'MT', - 'MN', 'NO', 'PL', 'PT', 'RO', 'RU', 'RS', 'SI', 'SK', 'ES', 'SE', 'CH', 'SY', - 'TJ', 'LK', 'TH', 'TR', 'TW', 'UA', 'GB', 'UZ', 'VN', 'KR', 'IE', 'PK', 'MV', - 'ZA', 'EPO', 'NP', 'NG', 'ET', 'SN', 'BRAI', 'TM', 'ML', 'TZ', 'TG', 'KE', 'BW', - 'PH', 'MD', 'ID', 'MY', 'BN', 'IN', 'IS', 'NEC_VNDR_JP', name='layouts'), - nullable=True), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'computer_accessory', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column( + 'layout', + sa.Enum( + 'US', + 'AF', + 'ARA', + 'AL', + 'AM', + 'AT', + 'AU', + 'AZ', + 'BY', + 'BE', + 'BD', + 'BA', + 'BR', + 'BG', + 'DZ', + 'MA', + 'CM', + 'MM', + 'CA', + 'CD', + 'CN', + 'HR', + 'CZ', + 'DK', + 'NL', + 'BT', + 'EE', + 'IR', + 'IQ', + 'FO', + 'FI', + 'FR', + 'GH', + 'GN', + 'GE', + 'DE', + 'GR', + 'HU', + 'IL', + 'IT', + 'JP', + 'KG', + 'KH', + 'KZ', + 'LA', + 'LATAM', + 'LT', + 'LV', + 'MAO', + 'ME', + 'MK', + 'MT', + 'MN', + 'NO', + 'PL', + 'PT', + 'RO', + 'RU', + 'RS', + 'SI', + 'SK', + 'ES', + 'SE', + 'CH', + 'SY', + 'TJ', + 'LK', + 'TH', + 'TR', + 'TW', + 'UA', + 'GB', + 'UZ', + 'VN', + 'KR', + 'IE', + 'PK', + 'MV', + 'ZA', + 'EPO', + 'NP', + 'NG', + 'ET', + 'SN', + 'BRAI', + 'TM', + 'ML', + 'TZ', + 'TG', + 'KE', + 'BW', + 'PH', + 'MD', + 'ID', + 'MY', + 'BN', + 'IN', + 'IS', + 'NEC_VNDR_JP', + name='layouts', + ), + nullable=True, + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Device search table - op.create_table('device_search', - sa.Column('device_id', sa.BigInteger(), nullable=False), - sa.Column('properties', postgresql.TSVECTOR(), nullable=False), - sa.Column('tags', postgresql.TSVECTOR(), nullable=True), - sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('device_id'), - schema=f'{get_inv()}' - ) - op.create_index('properties gist', 'device_search', ['properties'], unique=False, postgresql_using='gist', - schema=f'{get_inv()}') - op.create_index('tags gist', 'device_search', ['tags'], unique=False, postgresql_using='gist', - schema=f'{get_inv()}') + op.create_table( + 'device_search', + sa.Column('device_id', sa.BigInteger(), nullable=False), + sa.Column('properties', postgresql.TSVECTOR(), nullable=False), + sa.Column('tags', postgresql.TSVECTOR(), nullable=True), + sa.ForeignKeyConstraint( + ['device_id'], [f'{get_inv()}.device.id'], ondelete='CASCADE' + ), + sa.PrimaryKeyConstraint('device_id'), + schema=f'{get_inv()}', + ) + op.create_index( + 'properties gist', + 'device_search', + ['properties'], + unique=False, + postgresql_using='gist', + schema=f'{get_inv()}', + ) + op.create_index( + 'tags gist', + 'device_search', + ['tags'], + unique=False, + postgresql_using='gist', + schema=f'{get_inv()}', + ) # Lot table - op.create_table('lot', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('name', citext.CIText(), nullable=False), - sa.Column('description', citext.CIText(), nullable=True, comment='A comment about the lot.'), - sa.Column('closed', sa.Boolean(), nullable=False, - comment='A closed lot cannot be modified anymore.'), - sa.Column('deposit', sa.Integer(), nullable=True), - sa.Column('owner_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('transfer_state', teal.db.IntEnum(TransferState), nullable=False, - comment='State of transfer for a given Lot of devices.\n '), - sa.Column('receiver_address', citext.CIText(), nullable=True), - sa.Column('deliverynote_address', citext.CIText(), nullable=True), - sa.ForeignKeyConstraint(['owner_id'], ['common.user.id'], ), - sa.ForeignKeyConstraint(['receiver_address'], ['common.user.ethereum_address'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) - op.create_index(op.f('ix_lot_created'), 'lot', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_lot_updated'), 'lot', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_table( + 'lot', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('name', citext.CIText(), nullable=False), + sa.Column( + 'description', + citext.CIText(), + nullable=True, + comment='A comment about the lot.', + ), + sa.Column( + 'closed', + sa.Boolean(), + nullable=False, + comment='A closed lot cannot be modified anymore.', + ), + sa.Column('deposit', sa.Integer(), nullable=True), + sa.Column('owner_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column( + 'transfer_state', + teal.db.IntEnum(TransferState), + nullable=False, + comment='State of transfer for a given Lot of devices.\n ', + ), + sa.Column('receiver_address', citext.CIText(), nullable=True), + sa.Column('deliverynote_address', citext.CIText(), nullable=True), + sa.ForeignKeyConstraint( + ['owner_id'], + ['common.user.id'], + ), + sa.ForeignKeyConstraint( + ['receiver_address'], + ['common.user.ethereum_address'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_lot_created'), 'lot', ['created'], unique=False, schema=f'{get_inv()}' + ) + op.create_index( + op.f('ix_lot_updated'), 'lot', ['updated'], unique=False, schema=f'{get_inv()}' + ) # Mobile table - op.create_table('mobile', - sa.Column('id', sa.BigInteger(), nullable=False), - sa.Column('imei', sa.BigInteger(), nullable=True, - comment='The International Mobile Equipment Identity of\n the smartphone as an integer.\n '), - sa.Column('meid', sa.Unicode(), nullable=True, - comment='The Mobile Equipment Identifier as a hexadecimal\n string.\n '), - sa.Column('ram_size', sa.Integer(), nullable=True, comment='The total of RAM of the device in MB.'), - sa.Column('data_storage_size', sa.Integer(), nullable=True, - comment='The total of data storage of the device in MB'), - sa.Column('display_size', sa.Float(decimal_return_scale=1), nullable=True, - comment='The total size of the device screen'), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'mobile', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column( + 'imei', + sa.BigInteger(), + nullable=True, + comment='The International Mobile Equipment Identity of\n the smartphone as an integer.\n ', + ), + sa.Column( + 'meid', + sa.Unicode(), + nullable=True, + comment='The Mobile Equipment Identifier as a hexadecimal\n string.\n ', + ), + sa.Column( + 'ram_size', + sa.Integer(), + nullable=True, + comment='The total of RAM of the device in MB.', + ), + sa.Column( + 'data_storage_size', + sa.Integer(), + nullable=True, + comment='The total of data storage of the device in MB', + ), + sa.Column( + 'display_size', + sa.Float(decimal_return_scale=1), + nullable=True, + comment='The total size of the device screen', + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Monitor table - op.create_table('monitor', - sa.Column('size', sa.Float(decimal_return_scale=1), nullable=False, - comment='The size of the monitor in inches.'), - sa.Column('technology', - sa.Enum('CRT', 'TFT', 'LED', 'PDP', 'LCD', 'OLED', 'AMOLED', name='displaytech'), - nullable=True, - comment='The technology the monitor uses to display\n the image.\n '), - sa.Column('resolution_width', sa.SmallInteger(), nullable=False, - comment='The maximum horizontal resolution the\n monitor can natively support in pixels.\n '), - sa.Column('resolution_height', sa.SmallInteger(), nullable=False, - comment='The maximum vertical resolution the\n monitor can natively support in pixels.\n '), - sa.Column('refresh_rate', sa.SmallInteger(), nullable=True), - sa.Column('contrast_ratio', sa.SmallInteger(), nullable=True), - sa.Column('touchable', sa.Boolean(), nullable=True, comment='Whether it is a touchscreen.'), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'monitor', + sa.Column( + 'size', + sa.Float(decimal_return_scale=1), + nullable=False, + comment='The size of the monitor in inches.', + ), + sa.Column( + 'technology', + sa.Enum( + 'CRT', 'TFT', 'LED', 'PDP', 'LCD', 'OLED', 'AMOLED', name='displaytech' + ), + nullable=True, + comment='The technology the monitor uses to display\n the image.\n ', + ), + sa.Column( + 'resolution_width', + sa.SmallInteger(), + nullable=False, + comment='The maximum horizontal resolution the\n monitor can natively support in pixels.\n ', + ), + sa.Column( + 'resolution_height', + sa.SmallInteger(), + nullable=False, + comment='The maximum vertical resolution the\n monitor can natively support in pixels.\n ', + ), + sa.Column('refresh_rate', sa.SmallInteger(), nullable=True), + sa.Column('contrast_ratio', sa.SmallInteger(), nullable=True), + sa.Column( + 'touchable', + sa.Boolean(), + nullable=True, + comment='Whether it is a touchscreen.', + ), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Networtking table - op.create_table('networking', - sa.Column('speed', sa.SmallInteger(), nullable=True, - comment='The maximum speed this network adapter can handle,\n in mbps.\n '), - sa.Column('wireless', sa.Boolean(), nullable=False, comment='Whether it is a wireless interface.'), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'networking', + sa.Column( + 'speed', + sa.SmallInteger(), + nullable=True, + comment='The maximum speed this network adapter can handle,\n in mbps.\n ', + ), + sa.Column( + 'wireless', + sa.Boolean(), + nullable=False, + comment='Whether it is a wireless interface.', + ), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Organization table - op.create_table('organization', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.agent.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'organization', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.agent.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Printer table - op.create_table('printer', - sa.Column('id', sa.BigInteger(), nullable=False), - sa.Column('wireless', sa.Boolean(), nullable=False, comment='Whether it is a wireless printer.'), - sa.Column('scanning', sa.Boolean(), nullable=False, - comment='Whether the printer has scanning capabilities.'), - sa.Column('technology', - sa.Enum('Toner', 'Inkjet', 'SolidInk', 'Dye', 'Thermal', name='printertechnology'), - nullable=True, comment='Technology used to print.'), - sa.Column('monochrome', sa.Boolean(), nullable=False, - comment='Whether the printer is only monochrome.'), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'printer', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column( + 'wireless', + sa.Boolean(), + nullable=False, + comment='Whether it is a wireless printer.', + ), + sa.Column( + 'scanning', + sa.Boolean(), + nullable=False, + comment='Whether the printer has scanning capabilities.', + ), + sa.Column( + 'technology', + sa.Enum( + 'Toner', + 'Inkjet', + 'SolidInk', + 'Dye', + 'Thermal', + name='printertechnology', + ), + nullable=True, + comment='Technology used to print.', + ), + sa.Column( + 'monochrome', + sa.Boolean(), + nullable=False, + comment='Whether the printer is only monochrome.', + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Proof table - op.create_table('proof', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('type', sa.Unicode(), nullable=False), - sa.Column('ethereum_hash', citext.CIText(), nullable=False), - sa.Column('device_id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) - op.create_index(op.f('ix_proof_created'), 'proof', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_proof_updated'), 'proof', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_table( + 'proof', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('type', sa.Unicode(), nullable=False), + sa.Column('ethereum_hash', citext.CIText(), nullable=False), + sa.Column('device_id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['device_id'], + [f'{get_inv()}.device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_proof_created'), + 'proof', + ['created'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_proof_updated'), + 'proof', + ['updated'], + unique=False, + schema=f'{get_inv()}', + ) # Action table - op.create_table('action', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('type', sa.Unicode(), nullable=False), - sa.Column('name', citext.CIText(), nullable=False, - comment='A name or title for the action. Used when searching\n for actions.\n '), - sa.Column('severity', teal.db.IntEnum(Severity), nullable=False, - comment='A flag evaluating the action execution. Ex. failed actions\n have the value `Severity.Error`. Devicehub uses 4 severity levels:\n\n * Info (Pass): default neutral severity. The action succeeded.\n * Notice: The action succeeded but it is raising awareness.\n Notices are not usually that important but something\n (good or bad) worth checking.\n * Warning: The action succeeded but there is something important\n to check negatively affecting the action.\n * Error (Fail): the action failed.\n\n Devicehub specially raises user awareness when an action\n has a Severity of ``Warning`` or greater.\n '), - sa.Column('closed', sa.Boolean(), nullable=False, - comment='Whether the author has finished the action.\n After this is set to True, no modifications are allowed.\n By default actions are closed when performed.\n '), - sa.Column('description', sa.Unicode(), nullable=False, comment='A comment about the action.'), - sa.Column('start_time', sa.TIMESTAMP(timezone=True), nullable=True, - comment='When the action starts. For some actions like\n reservations the time when they are available, for others like renting\n when the renting starts.\n '), - sa.Column('end_time', sa.TIMESTAMP(timezone=True), nullable=True, - comment='When the action ends. For some actions like reservations\n the time when they expire, for others like renting\n the time the end rents. For punctual actions it is the time \n they are performed; it differs with ``created`` in which\n created is the where the system received the action.\n '), - sa.Column('snapshot_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('author_id', postgresql.UUID(as_uuid=True), nullable=False, - comment='The user that recorded this action in the system.\n \n This does not necessarily has to be the person that produced\n the action in the real world. For that purpose see\n ``agent``.\n '), - sa.Column('agent_id', postgresql.UUID(as_uuid=True), nullable=False, - comment='The direct performer or driver of the action. \n e.g. John wrote a book.\n \n It can differ with the user that registered the action in the\n system, which can be in their behalf.\n '), - sa.Column('parent_id', sa.BigInteger(), nullable=True, - comment='For actions that are performed to components, \n the device parent at that time.\n \n For example: for a ``EraseBasic`` performed on a data storage, this\n would point to the computer that contained this data storage, if any.\n '), - sa.ForeignKeyConstraint(['agent_id'], [f'{get_inv()}.agent.id'], ), - sa.ForeignKeyConstraint(['author_id'], ['common.user.id'], ), - sa.ForeignKeyConstraint(['parent_id'], [f'{get_inv()}.computer.id'], ), - sa.ForeignKeyConstraint(['snapshot_id'], [f'{get_inv()}.snapshot.id'], name='snapshot_actions', - use_alter=True), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) - op.create_index(op.f('ix_action_created'), 'action', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_action_updated'), 'action', ['updated'], unique=False, schema=f'{get_inv()}') - op.create_index('ix_id', 'action', ['id'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') - op.create_index('ix_parent_id', 'action', ['parent_id'], unique=False, postgresql_using='hash', - schema=f'{get_inv()}') - op.create_index('ix_type', 'action', ['type'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') + op.create_table( + 'action', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('type', sa.Unicode(), nullable=False), + sa.Column( + 'name', + citext.CIText(), + nullable=False, + comment='A name or title for the action. Used when searching\n for actions.\n ', + ), + sa.Column( + 'severity', + teal.db.IntEnum(Severity), + nullable=False, + comment='A flag evaluating the action execution. Ex. failed actions\n have the value `Severity.Error`. Devicehub uses 4 severity levels:\n\n * Info (Pass): default neutral severity. The action succeeded.\n * Notice: The action succeeded but it is raising awareness.\n Notices are not usually that important but something\n (good or bad) worth checking.\n * Warning: The action succeeded but there is something important\n to check negatively affecting the action.\n * Error (Fail): the action failed.\n\n Devicehub specially raises user awareness when an action\n has a Severity of ``Warning`` or greater.\n ', + ), + sa.Column( + 'closed', + sa.Boolean(), + nullable=False, + comment='Whether the author has finished the action.\n After this is set to True, no modifications are allowed.\n By default actions are closed when performed.\n ', + ), + sa.Column( + 'description', + sa.Unicode(), + nullable=False, + comment='A comment about the action.', + ), + sa.Column( + 'start_time', + sa.TIMESTAMP(timezone=True), + nullable=True, + comment='When the action starts. For some actions like\n reservations the time when they are available, for others like renting\n when the renting starts.\n ', + ), + sa.Column( + 'end_time', + sa.TIMESTAMP(timezone=True), + nullable=True, + comment='When the action ends. For some actions like reservations\n the time when they expire, for others like renting\n the time the end rents. For punctual actions it is the time \n they are performed; it differs with ``created`` in which\n created is the where the system received the action.\n ', + ), + sa.Column('snapshot_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column( + 'author_id', + postgresql.UUID(as_uuid=True), + nullable=False, + comment='The user that recorded this action in the system.\n \n This does not necessarily has to be the person that produced\n the action in the real world. For that purpose see\n ``agent``.\n ', + ), + sa.Column( + 'agent_id', + postgresql.UUID(as_uuid=True), + nullable=False, + comment='The direct performer or driver of the action. \n e.g. John wrote a book.\n \n It can differ with the user that registered the action in the\n system, which can be in their behalf.\n ', + ), + sa.Column( + 'parent_id', + sa.BigInteger(), + nullable=True, + comment='For actions that are performed to components, \n the device parent at that time.\n \n For example: for a ``EraseBasic`` performed on a data storage, this\n would point to the computer that contained this data storage, if any.\n ', + ), + sa.ForeignKeyConstraint( + ['agent_id'], + [f'{get_inv()}.agent.id'], + ), + sa.ForeignKeyConstraint( + ['author_id'], + ['common.user.id'], + ), + sa.ForeignKeyConstraint( + ['parent_id'], + [f'{get_inv()}.computer.id'], + ), + sa.ForeignKeyConstraint( + ['snapshot_id'], + [f'{get_inv()}.snapshot.id'], + name='snapshot_actions', + use_alter=True, + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_action_created'), + 'action', + ['created'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_action_updated'), + 'action', + ['updated'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + 'ix_id', + 'action', + ['id'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) + op.create_index( + 'ix_parent_id', + 'action', + ['parent_id'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) + op.create_index( + 'ix_type', + 'action', + ['type'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) # Component table - op.create_table('component', - sa.Column('id', sa.BigInteger(), nullable=False), - sa.Column('parent_id', sa.BigInteger(), nullable=True), - sa.Column('focal_length', sa.SmallInteger(), nullable=True), - sa.Column('video_height', sa.SmallInteger(), nullable=True), - sa.Column('video_width', sa.Integer(), nullable=True), - sa.Column('horizontal_view_angle', sa.Integer(), nullable=True), - sa.Column('facing', sa.Enum('Front', 'Back', name='camerafacing'), nullable=True), - sa.Column('vertical_view_angle', sa.SmallInteger(), nullable=True), - sa.Column('video_stabilization', sa.Boolean(), nullable=True), - sa.Column('flash', sa.Boolean(), nullable=True), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.device.id'], ), - sa.ForeignKeyConstraint(['parent_id'], [f'{get_inv()}.computer.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) - op.create_index('parent_index', 'component', ['parent_id'], unique=False, postgresql_using='hash', - schema=f'{get_inv()}') + op.create_table( + 'component', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('parent_id', sa.BigInteger(), nullable=True), + sa.Column('focal_length', sa.SmallInteger(), nullable=True), + sa.Column('video_height', sa.SmallInteger(), nullable=True), + sa.Column('video_width', sa.Integer(), nullable=True), + sa.Column('horizontal_view_angle', sa.Integer(), nullable=True), + sa.Column( + 'facing', sa.Enum('Front', 'Back', name='camerafacing'), nullable=True + ), + sa.Column('vertical_view_angle', sa.SmallInteger(), nullable=True), + sa.Column('video_stabilization', sa.Boolean(), nullable=True), + sa.Column('flash', sa.Boolean(), nullable=True), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.device.id'], + ), + sa.ForeignKeyConstraint( + ['parent_id'], + [f'{get_inv()}.computer.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) + op.create_index( + 'parent_index', + 'component', + ['parent_id'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) # Deliverynote table - op.create_table('deliverynote', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('document_id', citext.CIText(), nullable=False), - sa.Column('creator_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('supplier_email', citext.CIText(), nullable=False), - sa.Column('receiver_address', citext.CIText(), nullable=False), - sa.Column('date', sa.DateTime(), nullable=False, comment='The date the DeliveryNote initiated'), - sa.Column('deposit', sa.Integer(), nullable=True), - sa.Column('expected_devices', postgresql.JSONB(astext_type=sa.Text()), nullable=False), - sa.Column('transferred_devices', sa.ARRAY(sa.Integer(), dimensions=1), nullable=True), - sa.Column('transfer_state', teal.db.IntEnum(TransferState), nullable=False, - comment='State of transfer for a given Lot of devices.\n '), - sa.Column('ethereum_address', citext.CIText(), nullable=True), - sa.Column('lot_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['creator_id'], ['common.user.id'], ), - sa.ForeignKeyConstraint(['lot_id'], [f'{get_inv()}.lot.id'], ), - sa.ForeignKeyConstraint(['receiver_address'], ['common.user.email'], ), - sa.ForeignKeyConstraint(['supplier_email'], ['common.user.email'], ), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('ethereum_address'), - schema=f'{get_inv()}' - ) - op.create_index(op.f('ix_deliverynote_created'), 'deliverynote', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_deliverynote_updated'), 'deliverynote', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_table( + 'deliverynote', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('document_id', citext.CIText(), nullable=False), + sa.Column('creator_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('supplier_email', citext.CIText(), nullable=False), + sa.Column('receiver_address', citext.CIText(), nullable=False), + sa.Column( + 'date', + sa.DateTime(), + nullable=False, + comment='The date the DeliveryNote initiated', + ), + sa.Column('deposit', sa.Integer(), nullable=True), + sa.Column( + 'expected_devices', postgresql.JSONB(astext_type=sa.Text()), nullable=False + ), + sa.Column( + 'transferred_devices', sa.ARRAY(sa.Integer(), dimensions=1), nullable=True + ), + sa.Column( + 'transfer_state', + teal.db.IntEnum(TransferState), + nullable=False, + comment='State of transfer for a given Lot of devices.\n ', + ), + sa.Column('ethereum_address', citext.CIText(), nullable=True), + sa.Column('lot_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['creator_id'], + ['common.user.id'], + ), + sa.ForeignKeyConstraint( + ['lot_id'], + [f'{get_inv()}.lot.id'], + ), + sa.ForeignKeyConstraint( + ['receiver_address'], + ['common.user.email'], + ), + sa.ForeignKeyConstraint( + ['supplier_email'], + ['common.user.email'], + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('ethereum_address'), + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_deliverynote_created'), + 'deliverynote', + ['created'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_deliverynote_updated'), + 'deliverynote', + ['updated'], + unique=False, + schema=f'{get_inv()}', + ) # Individual table - op.create_table('individual', - sa.Column('active_org_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['active_org_id'], [f'{get_inv()}.organization.id'], ), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.agent.id'], ), - sa.ForeignKeyConstraint(['user_id'], ['common.user.id'], ), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('user_id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'individual', + sa.Column('active_org_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['active_org_id'], + [f'{get_inv()}.organization.id'], + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.agent.id'], + ), + sa.ForeignKeyConstraint( + ['user_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('user_id'), + schema=f'{get_inv()}', + ) # Lot device table - op.create_table('lot_device', - sa.Column('device_id', sa.BigInteger(), nullable=False), - sa.Column('lot_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('author_id', postgresql.UUID(as_uuid=True), nullable=False, - comment='The user that put the device in the lot.'), - sa.ForeignKeyConstraint(['author_id'], ['common.user.id'], ), - sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id'], ), - sa.ForeignKeyConstraint(['lot_id'], [f'{get_inv()}.lot.id'], ), - sa.PrimaryKeyConstraint('device_id', 'lot_id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'lot_device', + sa.Column('device_id', sa.BigInteger(), nullable=False), + sa.Column('lot_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('created', sa.DateTime(), nullable=False), + sa.Column( + 'author_id', + postgresql.UUID(as_uuid=True), + nullable=False, + comment='The user that put the device in the lot.', + ), + sa.ForeignKeyConstraint( + ['author_id'], + ['common.user.id'], + ), + sa.ForeignKeyConstraint( + ['device_id'], + [f'{get_inv()}.device.id'], + ), + sa.ForeignKeyConstraint( + ['lot_id'], + [f'{get_inv()}.lot.id'], + ), + sa.PrimaryKeyConstraint('device_id', 'lot_id'), + schema=f'{get_inv()}', + ) # Path table - op.create_table('path', - sa.Column('id', postgresql.UUID(as_uuid=True), server_default=sa.text('gen_random_uuid()'), - nullable=False), - sa.Column('lot_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('path', sqlalchemy_utils.types.ltree.LtreeType(), nullable=False), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=True, comment='When Devicehub created this.'), - sa.ForeignKeyConstraint(['lot_id'], [f'{get_inv()}.lot.id'], ), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('path', deferrable='True', initially='immediate', name='path_unique'), - schema=f'{get_inv()}' - ) - op.create_index('lot_id_index', 'path', ['lot_id'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') - op.create_index('path_btree', 'path', ['path'], unique=False, postgresql_using='btree', schema=f'{get_inv()}') - op.create_index('path_gist', 'path', ['path'], unique=False, postgresql_using='gist', schema=f'{get_inv()}') + op.create_table( + 'path', + sa.Column( + 'id', + postgresql.UUID(as_uuid=True), + server_default=sa.text('gen_random_uuid()'), + nullable=False, + ), + sa.Column('lot_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('path', sqlalchemy_utils.types.ltree.LtreeType(), nullable=False), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=True, + comment='When Devicehub created this.', + ), + sa.ForeignKeyConstraint( + ['lot_id'], + [f'{get_inv()}.lot.id'], + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint( + 'path', deferrable='True', initially='immediate', name='path_unique' + ), + schema=f'{get_inv()}', + ) + op.create_index( + 'lot_id_index', + 'path', + ['lot_id'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) + op.create_index( + 'path_btree', + 'path', + ['path'], + unique=False, + postgresql_using='btree', + schema=f'{get_inv()}', + ) + op.create_index( + 'path_gist', + 'path', + ['path'], + unique=False, + postgresql_using='gist', + schema=f'{get_inv()}', + ) # Proof recycling table - op.create_table('proof_recycling', - sa.Column('collection_point', citext.CIText(), nullable=False), - sa.Column('date', sa.DateTime(), nullable=False), - sa.Column('contact', citext.CIText(), nullable=False), - sa.Column('ticket', citext.CIText(), nullable=False), - sa.Column('gps_location', citext.CIText(), nullable=False), - sa.Column('recycler_code', citext.CIText(), nullable=False), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'proof_recycling', + sa.Column('collection_point', citext.CIText(), nullable=False), + sa.Column('date', sa.DateTime(), nullable=False), + sa.Column('contact', citext.CIText(), nullable=False), + sa.Column('ticket', citext.CIText(), nullable=False), + sa.Column('gps_location', citext.CIText(), nullable=False), + sa.Column('recycler_code', citext.CIText(), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.proof.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Proof reuse table - op.create_table('proof_reuse', - sa.Column('receiver_segment', citext.CIText(), nullable=False), - sa.Column('id_receipt', citext.CIText(), nullable=False), - sa.Column('supplier_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('receiver_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('price', sa.Integer(), nullable=True), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), - sa.ForeignKeyConstraint(['receiver_id'], ['common.user.id'], ), - sa.ForeignKeyConstraint(['supplier_id'], ['common.user.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'proof_reuse', + sa.Column('receiver_segment', citext.CIText(), nullable=False), + sa.Column('id_receipt', citext.CIText(), nullable=False), + sa.Column('supplier_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('receiver_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('price', sa.Integer(), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.proof.id'], + ), + sa.ForeignKeyConstraint( + ['receiver_id'], + ['common.user.id'], + ), + sa.ForeignKeyConstraint( + ['supplier_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Proof transfer table - op.create_table('proof_transfer', - sa.Column('supplier_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('receiver_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('deposit', sa.Integer(), nullable=True), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), - sa.ForeignKeyConstraint(['receiver_id'], ['common.user.id'], ), - sa.ForeignKeyConstraint(['supplier_id'], ['common.user.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'proof_transfer', + sa.Column('supplier_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('receiver_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('deposit', sa.Integer(), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.proof.id'], + ), + sa.ForeignKeyConstraint( + ['receiver_id'], + ['common.user.id'], + ), + sa.ForeignKeyConstraint( + ['supplier_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Tag table - op.create_table('tag', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', citext.CIText(), nullable=False, comment='The ID of the tag.'), - sa.Column('org_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('provider', teal.db.URL(), nullable=True, - comment='The tag provider URL. If None, the provider is\n this Devicehub.\n '), - sa.Column('device_id', sa.BigInteger(), nullable=True), - sa.Column('secondary', citext.CIText(), nullable=True, - comment='A secondary identifier for this tag. \n It has the same constraints as the main one. Only needed in special cases.\n '), - sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id'], ondelete='SET NULL'), - sa.ForeignKeyConstraint(['org_id'], [f'{get_inv()}.organization.id'], ), - sa.PrimaryKeyConstraint('id', 'org_id'), - sa.UniqueConstraint('id', 'org_id', name='one tag id per organization'), - sa.UniqueConstraint('secondary', 'org_id', name='one secondary tag per organization'), - schema=f'{get_inv()}' - ) - op.create_index('device_id_index', 'tag', ['device_id'], unique=False, postgresql_using='hash', - schema=f'{get_inv()}') - op.create_index(op.f('ix_tag_created'), 'tag', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_tag_secondary'), 'tag', ['secondary'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_tag_updated'), 'tag', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_table( + 'tag', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column('id', citext.CIText(), nullable=False, comment='The ID of the tag.'), + sa.Column('org_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column( + 'provider', + teal.db.URL(), + nullable=True, + comment='The tag provider URL. If None, the provider is\n this Devicehub.\n ', + ), + sa.Column('device_id', sa.BigInteger(), nullable=True), + sa.Column( + 'secondary', + citext.CIText(), + nullable=True, + comment='A secondary identifier for this tag. \n It has the same constraints as the main one. Only needed in special cases.\n ', + ), + sa.ForeignKeyConstraint( + ['device_id'], [f'{get_inv()}.device.id'], ondelete='SET NULL' + ), + sa.ForeignKeyConstraint( + ['org_id'], + [f'{get_inv()}.organization.id'], + ), + sa.PrimaryKeyConstraint('id', 'org_id'), + sa.UniqueConstraint('id', 'org_id', name='one tag id per organization'), + sa.UniqueConstraint( + 'secondary', 'org_id', name='one secondary tag per organization' + ), + schema=f'{get_inv()}', + ) + op.create_index( + 'device_id_index', + 'tag', + ['device_id'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_tag_created'), 'tag', ['created'], unique=False, schema=f'{get_inv()}' + ) + op.create_index( + op.f('ix_tag_secondary'), + 'tag', + ['secondary'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_tag_updated'), 'tag', ['updated'], unique=False, schema=f'{get_inv()}' + ) # ActionComponent table - op.create_table('action_component', - sa.Column('device_id', sa.BigInteger(), nullable=False), - sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['action_id'], [f'{get_inv()}.action.id'], ), - sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('device_id', 'action_id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'action_component', + sa.Column('device_id', sa.BigInteger(), nullable=False), + sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['action_id'], + [f'{get_inv()}.action.id'], + ), + sa.ForeignKeyConstraint( + ['device_id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('device_id', 'action_id'), + schema=f'{get_inv()}', + ) # Action device table - op.create_table('action_device', - sa.Column('device_id', sa.BigInteger(), nullable=False), - sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['action_id'], [f'{get_inv()}.action.id'], ), - sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id'], ), - sa.PrimaryKeyConstraint('device_id', 'action_id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'action_device', + sa.Column('device_id', sa.BigInteger(), nullable=False), + sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['action_id'], + [f'{get_inv()}.action.id'], + ), + sa.ForeignKeyConstraint( + ['device_id'], + [f'{get_inv()}.device.id'], + ), + sa.PrimaryKeyConstraint('device_id', 'action_id'), + schema=f'{get_inv()}', + ) # ActionWithOneDevice table - op.create_table('action_with_one_device', - sa.Column('device_id', sa.BigInteger(), nullable=False), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id'], ), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) - op.create_index('action_one_device_id_index', 'action_with_one_device', ['device_id'], unique=False, - postgresql_using='hash', schema=f'{get_inv()}') + op.create_table( + 'action_with_one_device', + sa.Column('device_id', sa.BigInteger(), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['device_id'], + [f'{get_inv()}.device.id'], + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) + op.create_index( + 'action_one_device_id_index', + 'action_with_one_device', + ['device_id'], + unique=False, + postgresql_using='hash', + schema=f'{get_inv()}', + ) # Allocate table - op.create_table('allocate', - sa.Column('to_id', postgresql.UUID(), nullable=True), - sa.Column('organization', citext.CIText(), nullable=True), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.ForeignKeyConstraint(['to_id'], ['common.user.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'allocate', + sa.Column('to_id', postgresql.UUID(), nullable=True), + sa.Column('organization', citext.CIText(), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), + sa.ForeignKeyConstraint( + ['to_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # BAtter table - op.create_table('battery', - sa.Column('wireless', sa.Boolean(), nullable=True, - comment='If the battery can be charged wirelessly.'), - sa.Column('technology', sa.Enum('LiIon', 'NiCd', 'NiMH', 'LiPoly', 'LiFe', 'LiMn', 'Al', - name='batterytechnology'), nullable=True), - sa.Column('size', sa.Integer(), nullable=False, - comment='Maximum battery capacity by design, in mAh.\n\n Use BatteryTest\'s "size" to get the actual size of the battery.\n '), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'battery', + sa.Column( + 'wireless', + sa.Boolean(), + nullable=True, + comment='If the battery can be charged wirelessly.', + ), + sa.Column( + 'technology', + sa.Enum( + 'LiIon', + 'NiCd', + 'NiMH', + 'LiPoly', + 'LiFe', + 'LiMn', + 'Al', + name='batterytechnology', + ), + nullable=True, + ), + sa.Column( + 'size', + sa.Integer(), + nullable=False, + comment='Maximum battery capacity by design, in mAh.\n\n Use BatteryTest\'s "size" to get the actual size of the battery.\n ', + ), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # DataStorage table - op.create_table('data_storage', - sa.Column('size', sa.Integer(), nullable=True, comment='The size of the data-storage in MB.'), - sa.Column('interface', sa.Enum('ATA', 'USB', 'PCI', name='datastorageinterface'), nullable=True), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'data_storage', + sa.Column( + 'size', + sa.Integer(), + nullable=True, + comment='The size of the data-storage in MB.', + ), + sa.Column( + 'interface', + sa.Enum('ATA', 'USB', 'PCI', name='datastorageinterface'), + nullable=True, + ), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Deallocate table - op.create_table('deallocate', - sa.Column('from_id', postgresql.UUID(), nullable=True), - sa.Column('organization', citext.CIText(), nullable=True), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['from_id'], ['common.user.id'], ), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'deallocate', + sa.Column('from_id', postgresql.UUID(), nullable=True), + sa.Column('organization', citext.CIText(), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['from_id'], + ['common.user.id'], + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Display table - op.create_table('display', - sa.Column('size', sa.Float(decimal_return_scale=1), nullable=False, - comment='The size of the monitor in inches.'), - sa.Column('technology', - sa.Enum('CRT', 'TFT', 'LED', 'PDP', 'LCD', 'OLED', 'AMOLED', name='displaytech'), - nullable=True, - comment='The technology the monitor uses to display\n the image.\n '), - sa.Column('resolution_width', sa.SmallInteger(), nullable=False, - comment='The maximum horizontal resolution the\n monitor can natively support in pixels.\n '), - sa.Column('resolution_height', sa.SmallInteger(), nullable=False, - comment='The maximum vertical resolution the\n monitor can natively support in pixels.\n '), - sa.Column('refresh_rate', sa.SmallInteger(), nullable=True), - sa.Column('contrast_ratio', sa.SmallInteger(), nullable=True), - sa.Column('touchable', sa.Boolean(), nullable=True, comment='Whether it is a touchscreen.'), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'display', + sa.Column( + 'size', + sa.Float(decimal_return_scale=1), + nullable=False, + comment='The size of the monitor in inches.', + ), + sa.Column( + 'technology', + sa.Enum( + 'CRT', 'TFT', 'LED', 'PDP', 'LCD', 'OLED', 'AMOLED', name='displaytech' + ), + nullable=True, + comment='The technology the monitor uses to display\n the image.\n ', + ), + sa.Column( + 'resolution_width', + sa.SmallInteger(), + nullable=False, + comment='The maximum horizontal resolution the\n monitor can natively support in pixels.\n ', + ), + sa.Column( + 'resolution_height', + sa.SmallInteger(), + nullable=False, + comment='The maximum vertical resolution the\n monitor can natively support in pixels.\n ', + ), + sa.Column('refresh_rate', sa.SmallInteger(), nullable=True), + sa.Column('contrast_ratio', sa.SmallInteger(), nullable=True), + sa.Column( + 'touchable', + sa.Boolean(), + nullable=True, + comment='Whether it is a touchscreen.', + ), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # GraphiCard table - op.create_table('graphic_card', - sa.Column('memory', sa.SmallInteger(), nullable=True, - comment='The amount of memory of the Graphic Card in MB.'), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'graphic_card', + sa.Column( + 'memory', + sa.SmallInteger(), + nullable=True, + comment='The amount of memory of the Graphic Card in MB.', + ), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Membership table - op.create_table('membership', - sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, - comment='The last time Devicehub recorded a change for \n this thing.\n '), - sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), - nullable=False, comment='When Devicehub created this.'), - sa.Column('id', sa.Unicode(), nullable=True), - sa.Column('organization_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('individual_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['individual_id'], [f'{get_inv()}.individual.id'], ), - sa.ForeignKeyConstraint(['organization_id'], [f'{get_inv()}.organization.id'], ), - sa.PrimaryKeyConstraint('organization_id', 'individual_id'), - sa.UniqueConstraint('id', 'organization_id', name='One member id per organization.'), - schema=f'{get_inv()}' - ) - op.create_index(op.f('ix_membership_created'), 'membership', ['created'], unique=False, schema=f'{get_inv()}') - op.create_index(op.f('ix_membership_updated'), 'membership', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_table( + 'membership', + sa.Column( + 'updated', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n ', + ), + sa.Column( + 'created', + sa.TIMESTAMP(timezone=True), + server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='When Devicehub created this.', + ), + sa.Column('id', sa.Unicode(), nullable=True), + sa.Column('organization_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('individual_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['individual_id'], + [f'{get_inv()}.individual.id'], + ), + sa.ForeignKeyConstraint( + ['organization_id'], + [f'{get_inv()}.organization.id'], + ), + sa.PrimaryKeyConstraint('organization_id', 'individual_id'), + sa.UniqueConstraint( + 'id', 'organization_id', name='One member id per organization.' + ), + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_membership_created'), + 'membership', + ['created'], + unique=False, + schema=f'{get_inv()}', + ) + op.create_index( + op.f('ix_membership_updated'), + 'membership', + ['updated'], + unique=False, + schema=f'{get_inv()}', + ) # Migrate table - op.create_table('migrate', - sa.Column('other', teal.db.URL(), nullable=False, - comment='\n The URL of the Migrate in the other end.\n '), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'migrate', + sa.Column( + 'other', + teal.db.URL(), + nullable=False, + comment='\n The URL of the Migrate in the other end.\n ', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Motherboard table - op.create_table('motherboard', - sa.Column('slots', sa.SmallInteger(), nullable=True, comment='PCI slots the motherboard has.'), - sa.Column('usb', sa.SmallInteger(), nullable=True), - sa.Column('firewire', sa.SmallInteger(), nullable=True), - sa.Column('serial', sa.SmallInteger(), nullable=True), - sa.Column('pcmcia', sa.SmallInteger(), nullable=True), - sa.Column('bios_date', sa.Date(), nullable=True, comment='The date of the BIOS version.'), - sa.Column('ram_slots', sa.SmallInteger(), nullable=True), - sa.Column('ram_max_size', sa.Integer(), nullable=True), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'motherboard', + sa.Column( + 'slots', + sa.SmallInteger(), + nullable=True, + comment='PCI slots the motherboard has.', + ), + sa.Column('usb', sa.SmallInteger(), nullable=True), + sa.Column('firewire', sa.SmallInteger(), nullable=True), + sa.Column('serial', sa.SmallInteger(), nullable=True), + sa.Column('pcmcia', sa.SmallInteger(), nullable=True), + sa.Column( + 'bios_date', + sa.Date(), + nullable=True, + comment='The date of the BIOS version.', + ), + sa.Column('ram_slots', sa.SmallInteger(), nullable=True), + sa.Column('ram_max_size', sa.Integer(), nullable=True), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Network adapter - op.create_table('network_adapter', - sa.Column('speed', sa.SmallInteger(), nullable=True, - comment='The maximum speed this network adapter can handle,\n in mbps.\n '), - sa.Column('wireless', sa.Boolean(), nullable=False, comment='Whether it is a wireless interface.'), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'network_adapter', + sa.Column( + 'speed', + sa.SmallInteger(), + nullable=True, + comment='The maximum speed this network adapter can handle,\n in mbps.\n ', + ), + sa.Column( + 'wireless', + sa.Boolean(), + nullable=False, + comment='Whether it is a wireless interface.', + ), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Organize table - op.create_table('organize', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'organize', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Processor table - op.create_table('processor', - sa.Column('speed', sa.Float(), nullable=True, comment='The regular CPU speed.'), - sa.Column('cores', sa.SmallInteger(), nullable=True, comment='The number of regular cores.'), - sa.Column('threads', sa.SmallInteger(), nullable=True, comment='The number of threads per core.'), - sa.Column('address', sa.SmallInteger(), nullable=True, - comment='The address of the CPU: 8, 16, 32, 64, 128 or 256 bits.'), - sa.Column('abi', sa.Unicode(), nullable=True, - comment='The Application Binary Interface of the processor.'), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'processor', + sa.Column('speed', sa.Float(), nullable=True, comment='The regular CPU speed.'), + sa.Column( + 'cores', + sa.SmallInteger(), + nullable=True, + comment='The number of regular cores.', + ), + sa.Column( + 'threads', + sa.SmallInteger(), + nullable=True, + comment='The number of threads per core.', + ), + sa.Column( + 'address', + sa.SmallInteger(), + nullable=True, + comment='The address of the CPU: 8, 16, 32, 64, 128 or 256 bits.', + ), + sa.Column( + 'abi', + sa.Unicode(), + nullable=True, + comment='The Application Binary Interface of the processor.', + ), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # RamModule table - op.create_table('ram_module', - sa.Column('size', sa.SmallInteger(), nullable=True, comment='The capacity of the RAM stick.'), - sa.Column('speed', sa.SmallInteger(), nullable=True), - sa.Column('interface', - sa.Enum('SDRAM', 'DDR', 'DDR2', 'DDR3', 'DDR4', 'DDR5', 'DDR6', name='raminterface'), - nullable=True), - sa.Column('format', sa.Enum('DIMM', 'SODIMM', name='ramformat'), nullable=True), - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'ram_module', + sa.Column( + 'size', + sa.SmallInteger(), + nullable=True, + comment='The capacity of the RAM stick.', + ), + sa.Column('speed', sa.SmallInteger(), nullable=True), + sa.Column( + 'interface', + sa.Enum( + 'SDRAM', + 'DDR', + 'DDR2', + 'DDR3', + 'DDR4', + 'DDR5', + 'DDR6', + name='raminterface', + ), + nullable=True, + ), + sa.Column('format', sa.Enum('DIMM', 'SODIMM', name='ramformat'), nullable=True), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Receive table - op.create_table('receive', - sa.Column('role', - sa.Enum('Intermediary', 'FinalUser', 'CollectionPoint', 'RecyclingPoint', 'Transporter', - name='receiverrole'), nullable=False), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'receive', + sa.Column( + 'role', + sa.Enum( + 'Intermediary', + 'FinalUser', + 'CollectionPoint', + 'RecyclingPoint', + 'Transporter', + name='receiverrole', + ), + nullable=False, + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Sound card table - op.create_table('sound_card', - sa.Column('id', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.component.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'sound_card', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.component.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Benchmark table - op.create_table('benchmark', - sa.Column('elapsed', sa.Interval(), nullable=True), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action_with_one_device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'benchmark', + sa.Column('elapsed', sa.Interval(), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action_with_one_device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Erase basic table - op.create_table('erase_basic', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('method', sa.Enum('Shred', 'Disintegration', name='physicalerasuremethod'), - nullable=True), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action_with_one_device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'erase_basic', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column( + 'method', + sa.Enum('Shred', 'Disintegration', name='physicalerasuremethod'), + nullable=True, + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action_with_one_device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) - op.create_table('install', - sa.Column('elapsed', sa.Interval(), nullable=False), - sa.Column('address', sa.SmallInteger(), nullable=True), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action_with_one_device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'install', + sa.Column('elapsed', sa.Interval(), nullable=False), + sa.Column('address', sa.SmallInteger(), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action_with_one_device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Live table - op.create_table('live', - sa.Column('ip', teal.db.IP(), nullable=False, comment='The IP where the live was triggered.'), - sa.Column('subdivision_confidence', sa.SmallInteger(), nullable=False), - sa.Column('subdivision', - sa.Enum('AE-AJ', 'AE-AZ', 'AE-DU', 'AE-FU', 'AE-RK', 'AE-SH', 'AE-UQ', 'AF-BAL', 'AF-BAM', - 'AF-BDG', 'AF-BDS', 'AF-BGL', 'AF-FRAU', 'AF-FYB', 'AF-GHA', 'AF-GHO', 'AF-HEL', - 'AF-HER', 'AF-JOW', 'AF-KAB', 'AF-KANN', 'AF-KAP', 'AF-KDZ', 'AF-KNR', 'AF-LAG', - 'AF-LOW', 'AF-NAN', 'AF-NIM', 'AF-ORU', 'AF-PAR', 'AF-PIA', 'AF-PKA', 'AF-SAM', - 'AF-SAR', 'AF-TAK', 'AF-WAR', 'AF-ZAB', 'AL-BR', 'AL-BU', 'AL-DI', 'AL-DL', - 'AL-DR', 'AL-DV', 'AL-EL', 'AL-ER', 'AL-FR', 'AL-GJ', 'AL-GR', 'AL-HA', 'AL-KA', - 'AL-KB', 'AL-KC', 'AL-KO', 'AL-KR', 'AL-KU', 'AL-LA', 'AL-LB', 'AL-LE', 'AL-LU', - 'AL-MK', 'AL-MM', 'AL-MR', 'AL-MT', 'AL-PG', 'AL-PQ', 'AL-PR', 'AL-PU', 'AL-SH', - 'AL-SK', 'AL-SR', 'AL-TE', 'AL-TP', 'AL-TR', 'AL-VL', 'AM-AG', 'AM-AR', 'AM-AV', - 'AM-ER', 'AM-GR', 'AM-KT', 'AM-LO', 'AM-SH', 'AM-SU', 'AM-TV', 'AM-VD', 'AO-BGO', - 'AO-BGU', 'AO-BIE', 'AO-CAB', 'AO-CCU', 'AO-CNN', 'AO-CNO', 'AO-CUS', 'AO-HUA', - 'AO-HUI', 'AO-LNO', 'AO-LSU', 'AO-LUA', 'AO-MAL', 'AO-MOX', 'AO-NAM', 'AO-UIG', - 'AO-ZAI', 'AR-A', 'AR-B', 'AR-C', 'AR-D', 'AR-E', 'AR-F', 'AR-G', 'AR-H', 'AR-J', - 'AR-K', 'AR-L', 'AR-M', 'AR-N', 'AR-P', 'AR-Q', 'AR-R', 'AR-S', 'AR-T', 'AR-U', - 'AR-V', 'AR-W', 'AR-X', 'AR-Y', 'AR-Z', 'AT-1', 'AT-2', 'AT-3', 'AT-4', 'AT-5', - 'AT-6', 'AT-7', 'AT-8', 'AT-9', 'AU-CT', 'AU-NS', 'AU-NT', 'AU-QL', 'AU-SA', - 'AU-TS', 'AU-VI', 'AU-WA', 'AZ-AB', 'AZ-ABS', 'AZ-AGA', 'AZ-AGC', 'AZ-AGM', - 'AZ-AGS', 'AZ-AGU', 'AZ-AST', 'AZ-BA', 'AZ-BAB', 'AZ-BAL', 'AZ-BAR', 'AZ-BEY', - 'AZ-BIL', 'AZ-CAB', 'AZ-CAL', 'AZ-CUL', 'AZ-DAS', 'AZ-DAV', 'AZ-FUZ', 'AZ-GA', - 'AZ-GAD', 'AZ-GOR', 'AZ-GOY', 'AZ-HAC', 'AZ-IMI', 'AZ-ISM', 'AZ-KAL', 'AZ-KUR', - 'AZ-LA', 'AZ-LAC', 'AZ-LAN', 'AZ-LER', 'AZ-MAS', 'AZ-MI', 'AZ-MM', 'AZ-NA', - 'AZ-NEF', 'AZ-OGU', 'AZ-ORD', 'AZ-QAB', 'AZ-QAX', 'AZ-QAZ', 'AZ-QBA', 'AZ-QBI', - 'AZ-QOB', 'AZ-QUS', 'AZ-SA', 'AZ-SAB', 'AZ-SAD', 'AZ-SAH', 'AZ-SAK', 'AZ-SAL', - 'AZ-SAR', 'AZ-SAT', 'AZ-SIY', 'AZ-SKR', 'AZ-SM', 'AZ-SMI', 'AZ-SMX', 'AZ-SS', - 'AZ-SUS', 'AZ-TAR', 'AZ-TOV', 'AZ-UCA', 'AZ-XA', 'AZ-XAC', 'AZ-XAN', 'AZ-XCI', - 'AZ-XIZ', 'AZ-XVD', 'AZ-YAR', 'AZ-YE', 'AZ-YEV', 'AZ-ZAN', 'AZ-ZAQ', 'AZ-ZAR', - 'BA-BIH', 'BA-SRP', 'BD-01', 'BD-02', 'BD-03', 'BD-04', 'BD-05', 'BD-06', 'BD-07', - 'BD-08', 'BD-09', 'BD-1', 'BD-10', 'BD-11', 'BD-12', 'BD-13', 'BD-14', 'BD-15', - 'BD-16', 'BD-17', 'BD-18', 'BD-19', 'BD-2', 'BD-20', 'BD-21', 'BD-22', 'BD-23', - 'BD-24', 'BD-25', 'BD-26', 'BD-27', 'BD-28', 'BD-29', 'BD-3', 'BD-30', 'BD-31', - 'BD-32', 'BD-33', 'BD-34', 'BD-35', 'BD-36', 'BD-37', 'BD-38', 'BD-39', 'BD-4', - 'BD-40', 'BD-41', 'BD-42', 'BD-43', 'BD-44', 'BD-45', 'BD-46', 'BD-47', 'BD-48', - 'BD-49', 'BD-5', 'BD-50', 'BD-51', 'BD-52', 'BD-53', 'BD-54', 'BD-55', 'BD-56', - 'BD-57', 'BD-58', 'BD-59', 'BD-6', 'BD-60', 'BD-61', 'BD-62', 'BD-63', 'BD-64', - 'BE-BRU', 'BE-VAN', 'BE-VBR', 'BE-VLG', 'BE-VLI', 'BE-VOV', 'BE-VWV', 'BE-WAL', - 'BE-WBR', 'BE-WHT', 'BE-WLG', 'BE-WLX', 'BE-WNA', 'BF-BAL', 'BF-BAM', 'BF-BAN', - 'BF-BAZ', 'BF-BGR', 'BF-BLG', 'BF-BLK', 'BF-COM', 'BF-GAN', 'BF-GNA', 'BF-GOU', - 'BF-HOU', 'BF-IOB', 'BF-KAD', 'BF-KEN', 'BF-KMD', 'BF-KMP', 'BF-KOP', 'BF-KOS', - 'BF-KOT', 'BF-KOW', 'BF-LER', 'BF-LOR', 'BF-MOU', 'BF-NAM', 'BF-NAO', 'BF-NAY', - 'BF-NOU', 'BF-OUB', 'BF-OUD', 'BF-PAS', 'BF-PON', 'BF-SEN', 'BF-SIS', 'BF-SMT', - 'BF-SNG', 'BF-SOM', 'BF-SOR', 'BF-TAP', 'BF-TUI', 'BF-YAG', 'BF-YAT', 'BF-ZIR', - 'BF-ZON', 'BF-ZOU', 'BG-01', 'BG-02', 'BG-03', 'BG-04', 'BG-05', 'BG-06', 'BG-07', - 'BG-08', 'BG-09', 'BG-10', 'BG-11', 'BG-12', 'BG-13', 'BG-14', 'BG-15', 'BG-16', - 'BG-17', 'BG-18', 'BG-19', 'BG-20', 'BG-21', 'BG-22', 'BG-23', 'BG-24', 'BG-25', - 'BG-26', 'BG-27', 'BG-28', 'BH-01', 'BH-02', 'BH-03', 'BH-04', 'BH-05', 'BH-06', - 'BH-07', 'BH-08', 'BH-09', 'BH-10', 'BH-11', 'BH-12', 'BI-BB', 'BI-BJ', 'BI-BR', - 'BI-CA', 'BI-CI', 'BI-GI', 'BI-KI', 'BI-KR', 'BI-KY', 'BI-MA', 'BI-MU', 'BI-MW', - 'BI-MY', 'BI-NG', 'BI-RT', 'BI-RY', 'BJ-AK', 'BJ-AL', 'BJ-AQ', 'BJ-BO', 'BJ-CO', - 'BJ-DO', 'BJ-KO', 'BJ-LI', 'BJ-MO', 'BJ-OU', 'BJ-PL', 'BJ-ZO', 'BN-BE', 'BN-BM', - 'BN-TE', 'BN-TU', 'BO-B', 'BO-C', 'BO-H', 'BO-L', 'BO-N', 'BO-O', 'BO-P', 'BO-S', - 'BO-T', 'BR-AC', 'BR-AL', 'BR-AM', 'BR-AP', 'BR-BA', 'BR-CE', 'BR-DF', 'BR-ES', - 'BR-GO', 'BR-MA', 'BR-MG', 'BR-MS', 'BR-MT', 'BR-PA', 'BR-PB', 'BR-PE', 'BR-PI', - 'BR-PR', 'BR-RJ', 'BR-RN', 'BR-RO', 'BR-RR', 'BR-RS', 'BR-SC', 'BR-SE', 'BR-SP', - 'BR-TO', 'BS-AC', 'BS-BI', 'BS-CI', 'BS-EX', 'BS-FC', 'BS-FP', 'BS-GH', 'BS-GT', - 'BS-HI', 'BS-HR', 'BS-IN', 'BS-KB', 'BS-LI', 'BS-MG', 'BS-MH', 'BS-NB', 'BS-NP', - 'BS-RI', 'BS-RS', 'BS-SP', 'BS-SR', 'BT-11', 'BT-12', 'BT-13', 'BT-14', 'BT-15', - 'BT-21', 'BT-22', 'BT-23', 'BT-24', 'BT-31', 'BT-32', 'BT-33', 'BT-34', 'BT-41', - 'BT-42', 'BT-43', 'BT-44', 'BT-45', 'BT-GA', 'BT-TY', 'BW-CE', 'BW-CH', 'BW-GH', - 'BW-KG', 'BW-KL', 'BW-KW', 'BW-NE', 'BW-NG', 'BW-SE', 'BW-SO', 'BY-BR', 'BY-HO', - 'BY-HR', 'BY-MA', 'BY-MI', 'BY-VI', 'BZ-BZ', 'BZ-CY', 'BZ-CZL', 'BZ-OW', 'BZ-SC', - 'BZ-TOL', 'CA-AB', 'CA-BC', 'CA-MB', 'CA-NB', 'CA-NL', 'CA-NS', 'CA-NT', 'CA-NU', - 'CA-ON', 'CA-PE', 'CA-QC', 'CA-SK', 'CA-YT', 'CD-BC', 'CD-BN', 'CD-EQ', 'CD-KA', - 'CD-KE', 'CD-KN', 'CD-KW', 'CD-MA', 'CD-NK', 'CD-OR', 'CD-SK', 'CF-AC', 'CF-BB', - 'CF-BGF', 'CF-BK', 'CF-HK', 'CF-HM', 'CF-HS', 'CF-KB', 'CF-KG', 'CF-LB', 'CF-MB', - 'CF-MP', 'CF-NM', 'CF-OP', 'CF-SE', 'CF-UK', 'CF-VK', 'CG-11', 'CG-12', 'CG-13', - 'CG-14', 'CG-15', 'CG-2', 'CG-5', 'CG-7', 'CG-8', 'CG-9', 'CG-BZV', 'CH-AG', - 'CH-AI', 'CH-AR', 'CH-BE', 'CH-BL', 'CH-BS', 'CH-FR', 'CH-GE', 'CH-GL', 'CH-GR', - 'CH-JU', 'CH-LU', 'CH-NE', 'CH-NW', 'CH-OW', 'CH-SG', 'CH-SH', 'CH-SO', 'CH-SZ', - 'CH-TG', 'CH-TI', 'CH-UR', 'CH-VD', 'CH-VS', 'CH-ZG', 'CH-ZH', 'CI-01', 'CI-02', - 'CI-03', 'CI-04', 'CI-05', 'CI-06', 'CI-07', 'CI-08', 'CI-09', 'CI-10', 'CI-11', - 'CI-12', 'CI-13', 'CI-14', 'CI-15', 'CI-16', 'CL-AI', 'CL-AN', 'CL-AR', 'CL-AT', - 'CL-BI', 'CL-CO', 'CL-LI', 'CL-LL', 'CL-MA', 'CL-ML', 'CL-RM', 'CL-TA', 'CL-VS', - 'CM-AD', 'CM-CE', 'CM-EN', 'CM-ES', 'CM-LT', 'CM-NO', 'CM-NW', 'CM-OU', 'CM-SU', - 'CM-SW', 'CN-11', 'CN-12', 'CN-13', 'CN-14', 'CN-15', 'CN-21', 'CN-22', 'CN-23', - 'CN-31', 'CN-32', 'CN-33', 'CN-34', 'CN-35', 'CN-36', 'CN-37', 'CN-41', 'CN-42', - 'CN-43', 'CN-44', 'CN-45', 'CN-46', 'CN-50', 'CN-51', 'CN-52', 'CN-53', 'CN-54', - 'CN-61', 'CN-62', 'CN-63', 'CN-64', 'CN-65', 'CN-71', 'CN-91', 'CN-92', 'CO-AMA', - 'CO-ANT', 'CO-ARA', 'CO-ATL', 'CO-BOL', 'CO-BOY', 'CO-CAL', 'CO-CAQ', 'CO-CAS', - 'CO-CAU', 'CO-CES', 'CO-CHO', 'CO-COR', 'CO-CUN', 'CO-DC', 'CO-GUA', 'CO-GUV', - 'CO-HUI', 'CO-LAG', 'CO-MAG', 'CO-MET', 'CO-NAR', 'CO-NSA', 'CO-PUT', 'CO-QUI', - 'CO-RIS', 'CO-SAN', 'CO-SAP', 'CO-SUC', 'CO-TOL', 'CO-VAC', 'CO-VAU', 'CO-VID', - 'CR-A', 'CR-C', 'CR-G', 'CR-H', 'CR-L', 'CR-P', 'CR-SJ', 'CU-01', 'CU-02', - 'CU-03', 'CU-04', 'CU-05', 'CU-06', 'CU-07', 'CU-08', 'CU-09', 'CU-10', 'CU-11', - 'CU-12', 'CU-13', 'CU-14', 'CU-99', 'CV-B', 'CV-BR', 'CV-BV', 'CV-CA', 'CV-CR', - 'CV-CS', 'CV-FO', 'CV-MA', 'CV-MO', 'CV-PA', 'CV-PN', 'CV-PR', 'CV-RG', 'CV-S', - 'CV-SF', 'CV-SL', 'CV-SN', 'CV-SV', 'CV-TA', 'CY-01', 'CY-02', 'CY-03', 'CY-04', - 'CY-05', 'CY-06', 'CZ-JC', 'CZ-JM', 'CZ-KA', 'CZ-KR', 'CZ-LI', 'CZ-MO', 'CZ-OL', - 'CZ-PA', 'CZ-PL', 'CZ-PR', 'CZ-ST', 'CZ-US', 'CZ-VY', 'CZ-ZL', 'DE-BB', 'DE-BE', - 'DE-BW', 'DE-BY', 'DE-HB', 'DE-HE', 'DE-HH', 'DE-MV', 'DE-NI', 'DE-NW', 'DE-RP', - 'DE-SH', 'DE-SL', 'DE-SN', 'DE-ST', 'DE-TH', 'DJ-AS', 'DJ-DI', 'DJ-DJ', 'DJ-OB', - 'DJ-TA', 'DK-015', 'DK-020', 'DK-025', 'DK-030', 'DK-035', 'DK-040', 'DK-042', - 'DK-050', 'DK-055', 'DK-060', 'DK-065', 'DK-070', 'DK-076', 'DK-080', 'DK-101', - 'DK-147', 'DO-01', 'DO-02', 'DO-03', 'DO-04', 'DO-05', 'DO-06', 'DO-07', 'DO-08', - 'DO-09', 'DO-10', 'DO-11', 'DO-12', 'DO-13', 'DO-14', 'DO-15', 'DO-16', 'DO-17', - 'DO-18', 'DO-19', 'DO-20', 'DO-21', 'DO-22', 'DO-23', 'DO-24', 'DO-25', 'DO-26', - 'DO-27', 'DO-28', 'DO-29', 'DO-30', 'DZ-01', 'DZ-02', 'DZ-03', 'DZ-04', 'DZ-05', - 'DZ-06', 'DZ-07', 'DZ-08', 'DZ-09', 'DZ-10', 'DZ-11', 'DZ-12', 'DZ-13', 'DZ-14', - 'DZ-15', 'DZ-16', 'DZ-17', 'DZ-18', 'DZ-19', 'DZ-20', 'DZ-21', 'DZ-22', 'DZ-23', - 'DZ-24', 'DZ-25', 'DZ-26', 'DZ-27', 'DZ-28', 'DZ-29', 'DZ-30', 'DZ-31', 'DZ-32', - 'DZ-33', 'DZ-34', 'DZ-35', 'DZ-36', 'DZ-37', 'DZ-38', 'DZ-39', 'DZ-40', 'DZ-41', - 'DZ-42', 'DZ-43', 'DZ-44', 'DZ-45', 'DZ-46', 'DZ-47', 'DZ-48', 'EC-A', 'EC-B', - 'EC-C', 'EC-D', 'EC-E', 'EC-F', 'EC-G', 'EC-H', 'EC-I', 'EC-L', 'EC-M', 'EC-N', - 'EC-O', 'EC-P', 'EC-R', 'EC-S', 'EC-T', 'EC-U', 'EC-W', 'EC-X', 'EC-Y', 'EC-Z', - 'EE-37', 'EE-39', 'EE-44', 'EE-49', 'EE-51', 'EE-57', 'EE-59', 'EE-65', 'EE-67', - 'EE-70', 'EE-74', 'EE-78', 'EE-82', 'EE-84', 'EE-86', 'EG-ALX', 'EG-ASN', - 'EG-AST', 'EG-BA', 'EG-BH', 'EG-BNS', 'EG-C', 'EG-DK', 'EG-DT', 'EG-FYM', 'EG-GH', - 'EG-GZ', 'EG-IS', 'EG-JS', 'EG-KB', 'EG-KFS', 'EG-KN', 'EG-MN', 'EG-MNF', 'EG-MT', - 'EG-PTS', 'EG-SHG', 'EG-SHR', 'EG-SIN', 'EG-SUZ', 'EG-WAD', 'ER-AN', 'ER-DK', - 'ER-DU', 'ER-GB', 'ER-MA', 'ER-SK', 'ES-A', 'ES-AB', 'ES-AL', 'ES-AN', 'ES-AR', - 'ES-AV', 'ES-B', 'ES-BA', 'ES-BI', 'ES-BU', 'ES-C', 'ES-CA', 'ES-CC', 'ES-CE', - 'ES-CL', 'ES-CM', 'ES-CN', 'ES-CO', 'ES-CR', 'ES-CS', 'ES-CT', 'ES-CU', 'ES-EX', - 'ES-GA', 'ES-GC', 'ES-GI', 'ES-GR', 'ES-GU', 'ES-H', 'ES-HU', 'ES-J', 'ES-L', - 'ES-LE', 'ES-LO', 'ES-LU', 'ES-M', 'ES-MA', 'ES-ML', 'ES-MU', 'ES-NA', 'ES-O', - 'ES-OR', 'ES-P', 'ES-PM', 'ES-PO', 'ES-PV', 'ES-S', 'ES-SA', 'ES-SE', 'ES-SG', - 'ES-SO', 'ES-SS', 'ES-T', 'ES-TE', 'ES-TF', 'ES-TO', 'ES-V', 'ES-VA', 'ES-VC', - 'ES-VI', 'ES-Z', 'ES-ZA', 'ET-AA', 'ET-AF', 'ET-AM', 'ET-BE', 'ET-DD', 'ET-GA', - 'ET-HA', 'ET-OR', 'ET-SN', 'ET-SO', 'ET-TI', 'FI-AL', 'FI-ES', 'FI-IS', 'FI-LL', - 'FI-LS', 'FI-OL', 'FJ-C', 'FJ-E', 'FJ-N', 'FJ-R', 'FJ-W', 'FM-KSA', 'FM-PNI', - 'FM-TRK', 'FM-YAP', 'FR-01', 'FR-02', 'FR-03', 'FR-04', 'FR-05', 'FR-06', 'FR-07', - 'FR-08', 'FR-09', 'FR-10', 'FR-11', 'FR-12', 'FR-13', 'FR-14', 'FR-15', 'FR-16', - 'FR-17', 'FR-18', 'FR-19', 'FR-21', 'FR-22', 'FR-23', 'FR-24', 'FR-25', 'FR-26', - 'FR-27', 'FR-28', 'FR-29', 'FR-2A', 'FR-2B', 'FR-30', 'FR-31', 'FR-32', 'FR-33', - 'FR-34', 'FR-35', 'FR-36', 'FR-37', 'FR-38', 'FR-39', 'FR-40', 'FR-41', 'FR-42', - 'FR-43', 'FR-44', 'FR-45', 'FR-46', 'FR-47', 'FR-48', 'FR-49', 'FR-50', 'FR-51', - 'FR-52', 'FR-53', 'FR-54', 'FR-55', 'FR-56', 'FR-57', 'FR-58', 'FR-59', 'FR-60', - 'FR-61', 'FR-62', 'FR-63', 'FR-64', 'FR-65', 'FR-66', 'FR-67', 'FR-68', 'FR-69', - 'FR-70', 'FR-71', 'FR-72', 'FR-73', 'FR-74', 'FR-75', 'FR-76', 'FR-77', 'FR-78', - 'FR-79', 'FR-80', 'FR-81', 'FR-82', 'FR-83', 'FR-84', 'FR-85', 'FR-86', 'FR-87', - 'FR-88', 'FR-89', 'FR-90', 'FR-91', 'FR-92', 'FR-93', 'FR-94', 'FR-95', 'FR-A', - 'FR-B', 'FR-C', 'FR-D', 'FR-E', 'FR-F', 'FR-G', 'FR-GF', 'FR-GP', 'FR-H', 'FR-I', - 'FR-J', 'FR-K', 'FR-L', 'FR-M', 'FR-MQ', 'FR-N', 'FR-NC', 'FR-O', 'FR-P', 'FR-PF', - 'FR-PM', 'FR-Q', 'FR-R', 'FR-RE', 'FR-S', 'FR-T', 'FR-TF', 'FR-U', 'FR-V', - 'FR-WF', 'FR-YT', 'GA-1', 'GA-2', 'GA-3', 'GA-4', 'GA-5', 'GA-6', 'GA-7', 'GA-8', - 'GA-9', 'GB-ABD', 'GB-ABE', 'GB-AGB', 'GB-AGY', 'GB-ANS', 'GB-ANT', 'GB-ARD', - 'GB-ARM', 'GB-BAS', 'GB-BBD', 'GB-BDF', 'GB-BDG', 'GB-BEN', 'GB-BEX', 'GB-BFS', - 'GB-BGE', 'GB-BGW', 'GB-BIR', 'GB-BKM', 'GB-BLA', 'GB-BLY', 'GB-BMH', 'GB-BNB', - 'GB-BNE', 'GB-BNH', 'GB-BNS', 'GB-BOL', 'GB-BPL', 'GB-BRC', 'GB-BRD', 'GB-BRY', - 'GB-BST', 'GB-BUR', 'GB-CAM', 'GB-CAY', 'GB-CGN', 'GB-CGV', 'GB-CHA', 'GB-CHS', - 'GB-CKF', 'GB-CKT', 'GB-CLD', 'GB-CLK', 'GB-CLR', 'GB-CMA', 'GB-CMD', 'GB-CMN', - 'GB-CON', 'GB-COV', 'GB-CRF', 'GB-CRY', 'GB-CSR', 'GB-CWY', 'GB-DAL', 'GB-DBY', - 'GB-DEN', 'GB-DER', 'GB-DEV', 'GB-DGN', 'GB-DGY', 'GB-DNC', 'GB-DND', 'GB-DOR', - 'GB-DOW', 'GB-DRY', 'GB-DUD', 'GB-DUR', 'GB-EAL', 'GB-EAW', 'GB-EAY', 'GB-EDH', - 'GB-EDU', 'GB-ELN', 'GB-ELS', 'GB-ENF', 'GB-ENG', 'GB-ERW', 'GB-ERY', 'GB-ESS', - 'GB-ESX', 'GB-FAL', 'GB-FER', 'GB-FIF', 'GB-FLN', 'GB-GAT', 'GB-GBN', 'GB-GLG', - 'GB-GLS', 'GB-GRE', 'GB-GSY', 'GB-GWN', 'GB-HAL', 'GB-HAM', 'GB-HAV', 'GB-HCK', - 'GB-HEF', 'GB-HIL', 'GB-HLD', 'GB-HMF', 'GB-HNS', 'GB-HPL', 'GB-HRT', 'GB-HRW', - 'GB-HRY', 'GB-IOM', 'GB-IOS', 'GB-IOW', 'GB-ISL', 'GB-IVC', 'GB-JSY', 'GB-KEC', - 'GB-KEN', 'GB-KHL', 'GB-KIR', 'GB-KTT', 'GB-KWL', 'GB-LAN', 'GB-LBH', 'GB-LCE', - 'GB-LDS', 'GB-LEC', 'GB-LEW', 'GB-LIN', 'GB-LIV', 'GB-LMV', 'GB-LND', 'GB-LRN', - 'GB-LSB', 'GB-LUT', 'GB-MAN', 'GB-MDB', 'GB-MDW', 'GB-MFT', 'GB-MIK', 'GB-MLN', - 'GB-MON', 'GB-MRT', 'GB-MRY', 'GB-MTY', 'GB-MYL', 'GB-NAY', 'GB-NBL', 'GB-NDN', - 'GB-NEL', 'GB-NET', 'GB-NFK', 'GB-NGM', 'GB-NIR', 'GB-NLK', 'GB-NLN', 'GB-NSM', - 'GB-NTA', 'GB-NTH', 'GB-NTL', 'GB-NTT', 'GB-NTY', 'GB-NWM', 'GB-NWP', 'GB-NYK', - 'GB-NYM', 'GB-OLD', 'GB-OMH', 'GB-ORK', 'GB-OXF', 'GB-PEM', 'GB-PKN', 'GB-PLY', - 'GB-POL', 'GB-POR', 'GB-POW', 'GB-PTE', 'GB-RCC', 'GB-RCH', 'GB-RCT', 'GB-RDB', - 'GB-RDG', 'GB-RFW', 'GB-RIC', 'GB-ROT', 'GB-RUT', 'GB-SAW', 'GB-SAY', 'GB-SCB', - 'GB-SCT', 'GB-SFK', 'GB-SFT', 'GB-SGC', 'GB-SHF', 'GB-SHN', 'GB-SHR', 'GB-SKP', - 'GB-SLF', 'GB-SLG', 'GB-SLK', 'GB-SND', 'GB-SOL', 'GB-SOM', 'GB-SOS', 'GB-SRY', - 'GB-STB', 'GB-STE', 'GB-STG', 'GB-STH', 'GB-STN', 'GB-STS', 'GB-STT', 'GB-STY', - 'GB-SWA', 'GB-SWD', 'GB-SWK', 'GB-TAM', 'GB-TFW', 'GB-THR', 'GB-TOB', 'GB-TOF', - 'GB-TRF', 'GB-TWH', 'GB-UKM', 'GB-VGL', 'GB-WAR', 'GB-WBK', 'GB-WDU', 'GB-WFT', - 'GB-WGN', 'GB-WILL', 'GB-WKF', 'GB-WLL', 'GB-WLN', 'GB-WLS', 'GB-WLV', 'GB-WND', - 'GB-WNM', 'GB-WOK', 'GB-WOR', 'GB-WRL', 'GB-WRT', 'GB-WRX', 'GB-WSM', 'GB-WSX', - 'GB-YOR', 'GB-ZET', 'GE-AB', 'GE-AJ', 'GE-GU', 'GE-IM', 'GE-KA', 'GE-KK', 'GE-MM', - 'GE-RL', 'GE-SJ', 'GE-SK', 'GE-SZ', 'GE-TB', 'GH-AA', 'GH-AH', 'GH-BA', 'GH-CP', - 'GH-EP', 'GH-NP', 'GH-TV', 'GH-UE', 'GH-UW', 'GH-WP', 'GM-B', 'GM-L', 'GM-M', - 'GM-N', 'GM-U', 'GM-W', 'GN-B', 'GN-BE', 'GN-BF', 'GN-BK', 'GN-C', 'GN-CO', - 'GN-D', 'GN-DB', 'GN-DI', 'GN-DL', 'GN-DU', 'GN-F', 'GN-FA', 'GN-FO', 'GN-FR', - 'GN-GA', 'GN-GU', 'GN-K', 'GN-KA', 'GN-KB', 'GN-KD; 2', 'GN-KE', 'GN-KN', 'GN-KO', - 'GN-KS', 'GN-L', 'GN-LA', 'GN-LE', 'GN-LO', 'GN-M', 'GN-MC', 'GN-MD', 'GN-ML', - 'GN-MM', 'GN-N', 'GN-NZ', 'GN-PI', 'GN-SI', 'GN-TE', 'GN-TO', 'GN-YO', 'GQ-AN', - 'GQ-BN', 'GQ-BS', 'GQ-C', 'GQ-CS', 'GQ-I', 'GQ-KN', 'GQ-LI', 'GQ-WN', 'GR-01', - 'GR-03', 'GR-04', 'GR-05', 'GR-06', 'GR-07', 'GR-11', 'GR-12', 'GR-13', 'GR-14', - 'GR-15', 'GR-16', 'GR-17', 'GR-21', 'GR-22', 'GR-23', 'GR-24', 'GR-31', 'GR-32', - 'GR-33', 'GR-34', 'GR-41', 'GR-42', 'GR-43', 'GR-44', 'GR-51', 'GR-52', 'GR-53', - 'GR-54', 'GR-55', 'GR-56', 'GR-57', 'GR-58', 'GR-59', 'GR-61', 'GR-62', 'GR-63', - 'GR-64', 'GR-69', 'GR-71', 'GR-72', 'GR-73', 'GR-81', 'GR-82', 'GR-83', 'GR-84', - 'GR-85', 'GR-91', 'GR-92', 'GR-93', 'GR-94', 'GR-A1', 'GR-I', 'GR-II', 'GR-III', - 'GR-IV', 'GR-IX', 'GR-V', 'GR-VI', 'GR-VII', 'GR-VIII', 'GR-X', 'GR-XI', 'GR-XII', - 'GR-XIII', 'GT-AV', 'GT-BV', 'GT-CM', 'GT-CQ', 'GT-ES', 'GT-GU', 'GT-HU', 'GT-IZ', - 'GT-JA', 'GT-JU', 'GT-PE', 'GT-PR', 'GT-QC', 'GT-QZ', 'GT-RE', 'GT-SA', 'GT-SM', - 'GT-SO', 'GT-SR', 'GT-SU', 'GT-TO', 'GT-ZA', 'GW-BA', 'GW-BL', 'GW-BM', 'GW-BS', - 'GW-CA', 'GW-GA', 'GW-L', 'GW-N', 'GW-OI', 'GW-QU', 'GW-S', 'GW-TO', 'GY-BA', - 'GY-CU', 'GY-DE', 'GY-EB', 'GY-ES', 'GY-MA', 'GY-PM', 'GY-PT', 'GY-UD', 'GY-UT', - 'HN-AT', 'HN-CH', 'HN-CL', 'HN-CM', 'HN-CP', 'HN-CR', 'HN-EP', 'HN-FM', 'HN-GD', - 'HN-IB', 'HN-IN', 'HN-LE', 'HN-LP', 'HN-OC', 'HN-OL', 'HN-SB', 'HN-VA', 'HN-YO', - 'HR-01', 'HR-02', 'HR-03', 'HR-04', 'HR-05', 'HR-06', 'HR-07', 'HR-08', 'HR-09', - 'HR-10', 'HR-11', 'HR-12', 'HR-13', 'HR-14', 'HR-15', 'HR-16', 'HR-17', 'HR-18', - 'HR-19', 'HR-20', 'HR-21', 'HT-AR', 'HT-CE', 'HT-GA', 'HT-ND', 'HT-NE', 'HT-NO', - 'HT-OU', 'HT-SD', 'HT-SE', 'HU-BA', 'HU-BC', 'HU-BE', 'HU-BK', 'HU-BU', 'HU-BZ', - 'HU-CS', 'HU-DE', 'HU-DU', 'HU-EG', 'HU-FE', 'HU-GS', 'HU-GY', 'HU-HB', 'HU-HE', - 'HU-HV', 'HU-JN', 'HU-KE', 'HU-KM', 'HU-KV', 'HU-MI', 'HU-NK', 'HU-NO', 'HU-NY', - 'HU-PE', 'HU-PS', 'HU-SD', 'HU-SF', 'HU-SH', 'HU-SK', 'HU-SN', 'HU-SO', 'HU-SS', - 'HU-ST', 'HU-SZ', 'HU-TB', 'HU-TO', 'HU-VA', 'HU-VE', 'HU-VM', 'HU-ZA', 'HU-ZE', - 'ID-AC', 'ID-BA', 'ID-BB', 'ID-BE', 'ID-BT', 'ID-GO', 'ID-IJ', 'ID-JA', 'ID-JB', - 'ID-JI', 'ID-JK', 'ID-JT', 'ID-JW', 'ID-KA', 'ID-KB', 'ID-KI', 'ID-KS', 'ID-KT', - 'ID-LA', 'ID-MA', 'ID-MU', 'ID-NB', 'ID-NT', 'ID-NU', 'ID-PA', 'ID-RI', 'ID-SA', - 'ID-SB', 'ID-SG', 'ID-SL', 'ID-SM', 'ID-SN', 'ID-SS', 'ID-ST', 'ID-SU', 'ID-YO', - 'IE-C', 'IE-C; 2', 'IE-CE', 'IE-CN', 'IE-CW', 'IE-D', 'IE-DL', 'IE-G', 'IE-KE', - 'IE-KK', 'IE-KY', 'IE-L', 'IE-LD', 'IE-LH', 'IE-LK', 'IE-LM', 'IE-LS', 'IE-M', - 'IE-MH', 'IE-MN', 'IE-MO', 'IE-OY', 'IE-RN', 'IE-SO', 'IE-TA', 'IE-U', 'IE-WD', - 'IE-WH', 'IE-WW', 'IE-WX', 'IL-D', 'IL-HA', 'IL-JM', 'IL-M', 'IL-TA', 'IL-Z', - 'IN-AN', 'IN-AP', 'IN-AR', 'IN-AS', 'IN-BR', 'IN-CH', 'IN-CT', 'IN-DD', 'IN-DL', - 'IN-DN', 'IN-GA', 'IN-GJ', 'IN-HP', 'IN-HR', 'IN-JH', 'IN-JK', 'IN-KA', 'IN-KL', - 'IN-LD', 'IN-MH', 'IN-ML', 'IN-MN', 'IN-MP', 'IN-MZ', 'IN-NL', 'IN-OR', 'IN-PB', - 'IN-PY', 'IN-RJ', 'IN-SK', 'IN-TN', 'IN-TR', 'IN-UL', 'IN-UP', 'IN-WB', 'IQ-AN', - 'IQ-AR', 'IQ-BA', 'IQ-BB', 'IQ-BG', 'IQ-DA', 'IQ-DI', 'IQ-DQ', 'IQ-KA', 'IQ-MA', - 'IQ-MU', 'IQ-NA', 'IQ-NI', 'IQ-QA', 'IQ-SD', 'IQ-SU', 'IQ-TS', 'IQ-WA', 'IR-01', - 'IR-02', 'IR-03', 'IR-04', 'IR-05', 'IR-06', 'IR-07', 'IR-08', 'IR-09', 'IR-10', - 'IR-11', 'IR-12', 'IR-13', 'IR-14', 'IR-15', 'IR-16', 'IR-17', 'IR-18', 'IR-19', - 'IR-20', 'IR-21', 'IR-22', 'IR-23', 'IR-24', 'IR-25', 'IR-26', 'IR-27', 'IR-28', - 'IS-0', 'IS-1', 'IS-2', 'IS-3', 'IS-4', 'IS-5', 'IS-6', 'IS-7', 'IS-8', 'IT-21', - 'IT-23', 'IT-25', 'IT-32', 'IT-34', 'IT-36', 'IT-42', 'IT-45', 'IT-52', 'IT-55', - 'IT-57', 'IT-62', 'IT-65', 'IT-67', 'IT-72', 'IT-75', 'IT-77', 'IT-78', 'IT-82', - 'IT-88', 'IT-AG', 'IT-AL', 'IT-AN', 'IT-AO', 'IT-AP', 'IT-AQ', 'IT-AR', 'IT-AT', - 'IT-AV', 'IT-BA', 'IT-BG', 'IT-BI', 'IT-BL', 'IT-BN', 'IT-BO', 'IT-BR', 'IT-BS', - 'IT-BZ', 'IT-CA', 'IT-CB', 'IT-CE', 'IT-CH', 'IT-CL', 'IT-CN', 'IT-CO', 'IT-CR', - 'IT-CS', 'IT-CT', 'IT-CZ', 'IT-DU', 'IT-EN', 'IT-FE', 'IT-FG', 'IT-FI', 'IT-FO', - 'IT-FR', 'IT-GE', 'IT-GO', 'IT-GR', 'IT-IM', 'IT-IS', 'IT-KR', 'IT-LC', 'IT-LE', - 'IT-LI', 'IT-LO', 'IT-LT', 'IT-LU', 'IT-MC', 'IT-ME', 'IT-MI', 'IT-MN', 'IT-MO', - 'IT-MS', 'IT-MT', 'IT-NA', 'IT-NO', 'IT-NU', 'IT-OR', 'IT-PA', 'IT-PC', 'IT-PD', - 'IT-PE', 'IT-PG', 'IT-PI', 'IT-PN', 'IT-PO', 'IT-PR', 'IT-PS', 'IT-PT', 'IT-PV', - 'IT-PZ', 'IT-RA', 'IT-RC', 'IT-RE', 'IT-RG', 'IT-RI', 'IT-RM', 'IT-RN', 'IT-RO', - 'IT-SA', 'IT-SI', 'IT-SO', 'IT-SP', 'IT-SR', 'IT-SS', 'IT-SV', 'IT-TA', 'IT-TE', - 'IT-TN', 'IT-TO', 'IT-TP', 'IT-TR', 'IT-TS', 'IT-TV', 'IT-VA', 'IT-VB', 'IT-VC', - 'IT-VE', 'IT-VI', 'IT-VR', 'IT-VT', 'IT-VV', 'JM-01', 'JM-02', 'JM-03', 'JM-04', - 'JM-05', 'JM-06', 'JM-07', 'JM-08', 'JM-09', 'JM-10', 'JM-11', 'JM-12', 'JM-13', - 'JM-14', 'JO-AJ', 'JO-AM', 'JO-AQ', 'JO-AT', 'JO-AZ', 'JO-BA', 'JO-IR', 'JO-JA', - 'JO-KA', 'JO-MA', 'JO-MD', 'JO-MN', 'JP-01', 'JP-02', 'JP-03', 'JP-04', 'JP-05', - 'JP-06', 'JP-07', 'JP-08', 'JP-09', 'JP-10', 'JP-11', 'JP-12', 'JP-13', 'JP-14', - 'JP-15', 'JP-16', 'JP-17', 'JP-18', 'JP-19', 'JP-20', 'JP-21', 'JP-22', 'JP-23', - 'JP-24', 'JP-25', 'JP-26', 'JP-27', 'JP-28', 'JP-29', 'JP-30', 'JP-31', 'JP-32', - 'JP-33', 'JP-34', 'JP-35', 'JP-36', 'JP-37', 'JP-38', 'JP-39', 'JP-40', 'JP-41', - 'JP-42', 'JP-43', 'JP-44', 'JP-45', 'JP-46', 'JP-47', 'KE-110', 'KE-200', - 'KE-300', 'KE-400', 'KE-500', 'KE-600', 'KE-700', 'KE-900', 'KG-B', 'KG-C', - 'KG-GB', 'KG-J', 'KG-N', 'KG-O', 'KG-T', 'KG-Y', 'KH-1', 'KH-10', 'KH-11', - 'KH-12', 'KH-13', 'KH-14', 'KH-15', 'KH-16', 'KH-17', 'KH-18', 'KH-19', 'KH-2', - 'KH-20', 'KH-21', 'KH-22', 'KH-23', 'KH-24', 'KH-3', 'KH-4', 'KH-5', 'KH-6', - 'KH-7', 'KH-8', 'KH-9', 'KI-G', 'KI-L', 'KI-P', 'KM-A', 'KM-G', 'KM-M', 'KP-CHA', - 'KP-HAB', 'KP-HAN', 'KP-HWB', 'KP-HWN', 'KP-KAE', 'KP-KAN', 'KP-NAJ', 'KP-NAM', - 'KP-PYB', 'KP-PYN', 'KP-PYO', 'KP-YAN', 'KR-11', 'KR-26', 'KR-27', 'KR-28', - 'KR-29', 'KR-30', 'KR-31', 'KR-41', 'KR-42', 'KR-43', 'KR-44', 'KR-45', 'KR-46', - 'KR-47', 'KR-48', 'KR-49', 'KW-AH', 'KW-FA', 'KW-HA', 'KW-JA', 'KW-KU', 'KZ-AKM', - 'KZ-AKT', 'KZ-ALA', 'KZ-ALM', 'KZ-AST', 'KZ-ATY', 'KZ-KAR', 'KZ-KUS', 'KZ-KZY', - 'KZ-MAN', 'KZ-PAV', 'KZ-SEV', 'KZ-VOS', 'KZ-YUZ', 'KZ-ZAP', 'KZ-ZHA', 'LA-AT', - 'LA-BK', 'LA-BL', 'LA-CH', 'LA-HO', 'LA-KH', 'LA-LM', 'LA-LP', 'LA-OU', 'LA-PH', - 'LA-SL', 'LA-SV', 'LA-VI', 'LA-VT', 'LA-XA', 'LA-XE', 'LA-XI', 'LA-XN', 'LB-AS', - 'LB-BA', 'LB-BI', 'LB-JA', 'LB-JL', 'LB-NA', 'LK-1', 'LK-11', 'LK-12', 'LK-13', - 'LK-2', 'LK-21', 'LK-22', 'LK-23', 'LK-3', 'LK-31', 'LK-32', 'LK-33', 'LK-4', - 'LK-41', 'LK-42', 'LK-43', 'LK-44', 'LK-45', 'LK-5', 'LK-51', 'LK-52', 'LK-53', - 'LK-6', 'LK-61', 'LK-62', 'LK-7', 'LK-71', 'LK-72', 'LK-8', 'LK-81', 'LK-82', - 'LK-9', 'LK-91', 'LK-92', 'LR-BG', 'LR-BM', 'LR-CM', 'LR-GB', 'LR-GG', 'LR-GK', - 'LR-LO', 'LR-MG', 'LR-MO', 'LR-MY', 'LR-NI', 'LR-RI', 'LR-SI', 'LS-A', 'LS-B', - 'LS-C', 'LS-D', 'LS-E', 'LS-F', 'LS-G', 'LS-H', 'LS-J', 'LS-K', 'LT-AL', 'LT-KL', - 'LT-KU', 'LT-MR', 'LT-PN', 'LT-SA', 'LT-TA', 'LT-TE', 'LT-UT', 'LT-VL', 'LU-D', - 'LU-G', 'LU-L', 'LV-AI', 'LV-AL', 'LV-BL', 'LV-BU', 'LV-CE', 'LV-DA', 'LV-DGV', - 'LV-DO', 'LV-GU', 'LV-JEL', 'LV-JK', 'LV-JL', 'LV-JUR', 'LV-KR', 'LV-KU', 'LV-LE', - 'LV-LM', 'LV-LPX', 'LV-LU', 'LV-MA', 'LV-OG', 'LV-PR', 'LV-RE', 'LV-REZ', 'LV-RI', - 'LV-RIX', 'LV-SA', 'LV-TA', 'LV-TU', 'LV-VE', 'LV-VEN', 'LV-VK', 'LV-VM', 'LY-BA', - 'LY-BU', 'LY-FA', 'LY-JA', 'LY-JG', 'LY-JU', 'LY-MI', 'LY-NA', 'LY-SF', 'LY-TB', - 'LY-WA', 'LY-WU', 'LY-ZA', 'MA-01', 'MA-02', 'MA-03', 'MA-04', 'MA-05', 'MA-06', - 'MA-07', 'MA-08', 'MA-09', 'MA-10', 'MA-11', 'MA-12', 'MA-13', 'MA-14', 'MA-15', - 'MA-16', 'MA-AGD', 'MA-ASZ', 'MA-AZI', 'MA-BAH', 'MA-BEM', 'MA-BER', 'MA-BES', - 'MA-BOD', 'MA-BOM', 'MA-CAS', 'MA-CHE', 'MA-CHI', 'MA-ERR', 'MA-ESI', 'MA-ESM', - 'MA-FES', 'MA-FIG', 'MA-GUE', 'MA-HAJ', 'MA-HAO', 'MA-HOC', 'MA-IFR', 'MA-JDI', - 'MA-JRA', 'MA-KEN', 'MA-KES', 'MA-KHE', 'MA-KHN', 'MA-KHO', 'MA-LAA', 'MA-LAR', - 'MA-MAR', 'MA-MEK', 'MA-MEL', 'MA-NAD', 'MA-OUA', 'MA-OUD', 'MA-OUJ', 'MA-RBA', - 'MA-SAF', 'MA-SEF', 'MA-SET', 'MA-SIK', 'MA-TAO', 'MA-TAR', 'MA-TAT', 'MA-TAZ', - 'MA-TET', 'MA-TIZ', 'MA-TNG', 'MA-TNT', 'MD-BA', 'MD-CA', 'MD-CH', 'MD-CU', - 'MD-ED', 'MD-GA', 'MD-LA', 'MD-OR', 'MD-SN', 'MD-SO', 'MD-TA', 'MD-TI', 'MD-UN', - 'MG-A', 'MG-D', 'MG-F', 'MG-M', 'MG-T', 'MG-U', 'MH-ALK', 'MH-ALL', 'MH-ARN', - 'MH-AUR', 'MH-EBO', 'MH-ENI', 'MH-JAL', 'MH-KIL', 'MH-KWA', 'MH-L', 'MH-LAE', - 'MH-LIB', 'MH-LIK', 'MH-MAJ', 'MH-MAL', 'MH-MEJ', 'MH-MIL', 'MH-NMK', 'MH-NMU', - 'MH-RON', 'MH-T', 'MH-UJA', 'MH-UJL', 'MH-UTI', 'MH-WTH', 'MH-WTJ', 'ML-1', - 'ML-2', 'ML-3', 'ML-4', 'ML-5', 'ML-6', 'ML-7', 'ML-8', 'ML-BKO', 'MM-01', - 'MM-02', 'MM-03', 'MM-04', 'MM-05', 'MM-06', 'MM-07', 'MM-11', 'MM-12', 'MM-13', - 'MM-14', 'MM-15', 'MM-16', 'MM-17', 'MN-035', 'MN-037', 'MN-039', 'MN-041', - 'MN-043', 'MN-046', 'MN-047', 'MN-049', 'MN-051', 'MN-053', 'MN-055', 'MN-057', - 'MN-059', 'MN-061', 'MN-063', 'MN-064', 'MN-065', 'MN-067', 'MN-069', 'MN-071', - 'MN-073', 'MN-1', 'MR-01', 'MR-02', 'MR-03', 'MR-04', 'MR-05', 'MR-06', 'MR-07', - 'MR-08', 'MR-09', 'MR-10', 'MR-11', 'MR-12', 'MR-NKC', 'MU-AG', 'MU-BL', 'MU-BR', - 'MU-CC', 'MU-CU', 'MU-FL', 'MU-GP', 'MU-MO', 'MU-PA', 'MU-PL', 'MU-PU', 'MU-PW', - 'MU-QB', 'MU-RO', 'MU-RR', 'MU-SA', 'MU-VP', 'MV-01', 'MV-02', 'MV-03', 'MV-04', - 'MV-05', 'MV-07', 'MV-08', 'MV-12', 'MV-13', 'MV-14', 'MV-17', 'MV-20', 'MV-23', - 'MV-24', 'MV-25', 'MV-26', 'MV-27', 'MV-28', 'MV-29', 'MV-MLE', 'MW-BA', 'MW-BL', - 'MW-C', 'MW-CK', 'MW-CR', 'MW-CT', 'MW-DE', 'MW-DO', 'MW-KR', 'MW-KS', 'MW-LI', - 'MW-LK', 'MW-MC', 'MW-MG', 'MW-MH', 'MW-MU', 'MW-MW', 'MW-MZ', 'MW-N', 'MW-NB', - 'MW-NI', 'MW-NK', 'MW-NS', 'MW-NU', 'MW-PH', 'MW-RU', 'MW-S', 'MW-SA', 'MW-TH', - 'MW-ZO', 'MX-AGU', 'MX-BCN', 'MX-BCS', 'MX-CAM', 'MX-CHH', 'MX-CHP', 'MX-COA', - 'MX-COL', 'MX-DIF', 'MX-DUR', 'MX-GRO', 'MX-GUA', 'MX-HID', 'MX-JAL', 'MX-MEX', - 'MX-MIC', 'MX-MOR', 'MX-NAY', 'MX-NLE', 'MX-OAX', 'MX-PUE', 'MX-QUE', 'MX-ROO', - 'MX-SIN', 'MX-SLP', 'MX-SON', 'MX-TAB', 'MX-TAM', 'MX-TLA', 'MX-VER', 'MX-YUC', - 'MX-ZAC', 'MY-A', 'MY-B', 'MY-C', 'MY-D', 'MY-J', 'MY-K', 'MY-L', 'MY-M', 'MY-N', - 'MY-P', 'MY-R', 'MY-SA', 'MY-SK', 'MY-T', 'MY-W', 'MZ-A', 'MZ-B', 'MZ-G', 'MZ-I', - 'MZ-L', 'MZ-MPM', 'MZ-N', 'MZ-P', 'MZ-Q', 'MZ-S', 'MZ-T', 'NA-CA', 'NA-ER', - 'NA-HA', 'NA-KA', 'NA-KH', 'NA-KU', 'NA-OD', 'NA-OH', 'NA-OK', 'NA-ON', 'NA-OS', - 'NA-OT', 'NA-OW', 'NE-1', 'NE-2', 'NE-3', 'NE-4', 'NE-5', 'NE-6', 'NE-7', 'NE-8', - 'NG-AB', 'NG-AD', 'NG-AK', 'NG-AN', 'NG-BA', 'NG-BE', 'NG-BO', 'NG-BY', 'NG-CR', - 'NG-DE', 'NG-EB', 'NG-ED', 'NG-EK', 'NG-EN', 'NG-FC', 'NG-GO', 'NG-IM', 'NG-JI', - 'NG-KD', 'NG-KE', 'NG-KN', 'NG-KO', 'NG-KT', 'NG-KW', 'NG-LA', 'NG-NA', 'NG-NI', - 'NG-OG', 'NG-ON', 'NG-OS', 'NG-OY', 'NG-PL', 'NG-RI', 'NG-SO', 'NG-TA', 'NG-YO', - 'NG-ZA', 'NI-AN', 'NI-AS', 'NI-BO', 'NI-CA', 'NI-CI', 'NI-CO', 'NI-ES', 'NI-GR', - 'NI-JI', 'NI-LE', 'NI-MD', 'NI-MN', 'NI-MS', 'NI-MT', 'NI-NS', 'NI-RI', 'NI-SJ', - 'NL-DR', 'NL-FL', 'NL-FR', 'NL-GE', 'NL-GR', 'NL-LI', 'NL-NB', 'NL-NH', 'NL-OV', - 'NL-UT', 'NL-ZE', 'NL-ZH', 'NO-01', 'NO-02', 'NO-03', 'NO-04', 'NO-05', 'NO-06', - 'NO-07', 'NO-08', 'NO-09', 'NO-10', 'NO-11', 'NO-12', 'NO-14', 'NO-15', 'NO-16', - 'NO-17', 'NO-18', 'NO-19', 'NO-20', 'NO-21', 'NO-22', 'NP-1', 'NP-2', 'NP-3', - 'NP-4', 'NP-5', 'NP-BA', 'NP-BH', 'NP-DH', 'NP-GA', 'NP-JA', 'NP-KA', 'NP-KO', - 'NP-LU', 'NP-MA', 'NP-ME', 'NP-NA', 'NP-RA', 'NP-SA', 'NP-SE', 'NZ-AUK', 'NZ-BOP', - 'NZ-CAN', 'NZ-GIS', 'NZ-HKB', 'NZ-MBH', 'NZ-MWT', 'NZ-N', 'NZ-NSN', 'NZ-NTL', - 'NZ-OTA', 'NZ-S', 'NZ-STL', 'NZ-TAS', 'NZ-TKI', 'NZ-WGN', 'NZ-WKO', 'NZ-WTC', - 'OM-BA', 'OM-DA', 'OM-JA', 'OM-MA', 'OM-MU', 'OM-SH', 'OM-WU', 'OM-ZA', 'PA-0', - 'PA-1', 'PA-2', 'PA-3', 'PA-4', 'PA-5', 'PA-6', 'PA-7', 'PA-8', 'PA-9', 'PE-AMA', - 'PE-ANC', 'PE-APU', 'PE-ARE', 'PE-AYA', 'PE-CAJ', 'PE-CAL', 'PE-CUS', 'PE-HUC', - 'PE-HUV', 'PE-ICA', 'PE-JUN', 'PE-LAL', 'PE-LAM', 'PE-LIM', 'PE-LOR', 'PE-MDD', - 'PE-MOQ', 'PE-PAS', 'PE-PIU', 'PE-PUN', 'PE-SAM', 'PE-TAC', 'PE-TUM', 'PE-UCA', - 'PG-CPK', 'PG-CPM', 'PG-EBR', 'PG-EHG', 'PG-EPW', 'PG-ESW', 'PG-GPK', 'PG-MBA', - 'PG-MPL', 'PG-MPM', 'PG-MRL', 'PG-NCD', 'PG-NIK', 'PG-NPP', 'PG-NSA', 'PG-SAN', - 'PG-SHM', 'PG-WBK', 'PG-WHM', 'PG-WPD', 'PH-00', 'PH-01', 'PH-02', 'PH-03', - 'PH-04', 'PH-05', 'PH-06', 'PH-07', 'PH-08', 'PH-09', 'PH-10', 'PH-11', 'PH-12', - 'PH-13', 'PH-14', 'PH-15', 'PH-ABR', 'PH-AGN', 'PH-AGS', 'PH-AKL', 'PH-ALB', - 'PH-ANT', 'PH-APA', 'PH-AUR', 'PH-BAN', 'PH-BAS', 'PH-BEN', 'PH-BIL', 'PH-BOH', - 'PH-BTG', 'PH-BTN', 'PH-BUK', 'PH-BUL', 'PH-CAG', 'PH-CAM', 'PH-CAN', 'PH-CAP', - 'PH-CAS', 'PH-CAT', 'PH-CAV', 'PH-CEB', 'PH-COM', 'PH-DAO', 'PH-DAS', 'PH-DAV', - 'PH-EAS', 'PH-GUI', 'PH-IFU', 'PH-ILI', 'PH-ILN', 'PH-ILS', 'PH-ISA', 'PH-KAL', - 'PH-LAG', 'PH-LAN', 'PH-LAS', 'PH-LEY', 'PH-LUN', 'PH-MAD', 'PH-MAG', 'PH-MAS', - 'PH-MDC', 'PH-MDR', 'PH-MOU', 'PH-MSC', 'PH-MSR', 'PH-NCO', 'PH-NEC', 'PH-NER', - 'PH-NSA', 'PH-NUE', 'PH-NUV', 'PH-PAM', 'PH-PAN', 'PH-PLW', 'PH-QUE', 'PH-QUI', - 'PH-RIZ', 'PH-ROM', 'PH-SAR', 'PH-SCO', 'PH-SIG', 'PH-SLE', 'PH-SLU', 'PH-SOR', - 'PH-SUK', 'PH-SUN', 'PH-SUR', 'PH-TAR', 'PH-TAW', 'PH-WSA', 'PH-ZAN', 'PH-ZAS', - 'PH-ZMB', 'PH-ZSI', 'PK-BA', 'PK-IS', 'PK-JK', 'PK-NA', 'PK-NW', 'PK-PB', 'PK-SD', - 'PK-TA', 'PL-DS', 'PL-KP', 'PL-LB', 'PL-LD', 'PL-LU', 'PL-MA', 'PL-MZ', 'PL-OP', - 'PL-PD', 'PL-PK', 'PL-PM', 'PL-SK', 'PL-SL', 'PL-WN', 'PL-WP', 'PL-ZP', 'PT-01', - 'PT-02', 'PT-03', 'PT-04', 'PT-05', 'PT-06', 'PT-07', 'PT-08', 'PT-09', 'PT-10', - 'PT-11', 'PT-12', 'PT-13', 'PT-14', 'PT-15', 'PT-16', 'PT-17', 'PT-18', 'PT-20', - 'PT-30', 'PY-1', 'PY-10', 'PY-11', 'PY-12', 'PY-13', 'PY-14', 'PY-15', 'PY-16', - 'PY-19', 'PY-2', 'PY-3', 'PY-4', 'PY-5', 'PY-6', 'PY-7', 'PY-8', 'PY-9', 'PY-ASU', - 'QA-DA', 'QA-GH', 'QA-JB', 'QA-JU', 'QA-KH', 'QA-MS', 'QA-RA', 'QA-US', 'QA-WA', - 'RO-AB', 'RO-AG', 'RO-AR', 'RO-B', 'RO-BC', 'RO-BH', 'RO-BN', 'RO-BR', 'RO-BT', - 'RO-BV', 'RO-BZ', 'RO-CJ', 'RO-CL', 'RO-CS', 'RO-CT', 'RO-CV', 'RO-DB', 'RO-DJ', - 'RO-GJ', 'RO-GL', 'RO-GR', 'RO-HD', 'RO-HR', 'RO-IF', 'RO-IL', 'RO-IS', 'RO-MH', - 'RO-MM', 'RO-MS', 'RO-NT', 'RO-OT', 'RO-PH', 'RO-SB', 'RO-SJ', 'RO-SM', 'RO-SV', - 'RO-TL', 'RO-TM', 'RO-TR', 'RO-VL', 'RO-VN', 'RO-VS', 'RU-AD', 'RU-AGB', 'RU-AL', - 'RU-ALT', 'RU-AMU', 'RU-ARK', 'RU-AST', 'RU-BA', 'RU-BEL', 'RU-BRY', 'RU-BU', - 'RU-CE', 'RU-CHE', 'RU-CHI', 'RU-CHU', 'RU-CU', 'RU-DA', 'RU-DU', 'RU-EVE', - 'RU-IN', 'RU-IRK', 'RU-IVA', 'RU-KAM', 'RU-KB', 'RU-KC', 'RU-KDA', 'RU-KEM', - 'RU-KGD', 'RU-KGN', 'RU-KHA', 'RU-KHM', 'RU-KIR', 'RU-KK', 'RU-KL', 'RU-KLU', - 'RU-KO', 'RU-KOP', 'RU-KOR', 'RU-KOS', 'RU-KR', 'RU-KRS', 'RU-KYA', 'RU-LEN', - 'RU-LIP', 'RU-MAG', 'RU-ME', 'RU-MO', 'RU-MOS', 'RU-MOW', 'RU-MUR', 'RU-NEN', - 'RU-NGR', 'RU-NIZ', 'RU-NVS', 'RU-OMS', 'RU-ORE', 'RU-ORL', 'RU-PER', 'RU-PNZ', - 'RU-PRI', 'RU-PSK', 'RU-ROS', 'RU-RYA', 'RU-SA', 'RU-SAK', 'RU-SAM', 'RU-SAR', - 'RU-SE', 'RU-SMO', 'RU-SPE', 'RU-STA', 'RU-SVE', 'RU-TA', 'RU-TAM', 'RU-TAY', - 'RU-TOM', 'RU-TUL', 'RU-TVE', 'RU-TY', 'RU-TYU', 'RU-ULY', 'RU-UOB', 'RU-VGG', - 'RU-VLA', 'RU-VLG', 'RU-VOR', 'RU-YAN', 'RU-YAR', 'RU-YEV', 'RW-B', 'RW-C', - 'RW-D', 'RW-E', 'RW-F', 'RW-G', 'RW-H', 'RW-I', 'RW-J', 'RW-K', 'RW-L', 'RW-M', - 'SA-01', 'SA-02', 'SA-03', 'SA-04', 'SA-05', 'SA-06', 'SA-07', 'SA-08', 'SA-09', - 'SA-10', 'SA-11', 'SA-12', 'SA-14', 'SB-CE', 'SB-CT', 'SB-GU', 'SB-IS', 'SB-MK', - 'SB-ML', 'SB-TE', 'SB-WE', 'SD-01', 'SD-02', 'SD-03', 'SD-04', 'SD-05', 'SD-06', - 'SD-07', 'SD-08', 'SD-09', 'SD-10', 'SD-11', 'SD-12', 'SD-13', 'SD-14', 'SD-15', - 'SD-16', 'SD-17', 'SD-18', 'SD-19', 'SD-20', 'SD-21', 'SD-22', 'SD-23', 'SD-24', - 'SD-25', 'SD-26', 'SE-AB', 'SE-AC', 'SE-BD', 'SE-C', 'SE-D', 'SE-E', 'SE-F', - 'SE-G', 'SE-H', 'SE-I', 'SE-K', 'SE-M', 'SE-N', 'SE-O', 'SE-S', 'SE-T', 'SE-U', - 'SE-W', 'SE-X', 'SE-Y', 'SE-Z', 'SH-AC', 'SH-SH', 'SH-TA', 'SI-01', 'SI-02', - 'SI-03', 'SI-04', 'SI-05', 'SI-06', 'SI-07', 'SI-08', 'SI-09', 'SI-10', 'SI-11', - 'SI-12', 'SK-BC', 'SK-BL', 'SK-KI', 'SK-NI', 'SK-PV', 'SK-TA', 'SK-TC', 'SK-ZI', - 'SL-E', 'SL-N', 'SL-S', 'SL-W', 'SN-DB', 'SN-DK', 'SN-FK', 'SN-KD', 'SN-KL', - 'SN-LG', 'SN-SL', 'SN-TC', 'SN-TH', 'SN-ZG', 'SO-AW', 'SO-BK', 'SO-BN', 'SO-BR', - 'SO-BY', 'SO-GA', 'SO-GE', 'SO-HI', 'SO-JD', 'SO-JH', 'SO-MU', 'SO-NU', 'SO-SA', - 'SO-SD', 'SO-SH', 'SO-SO', 'SO-TO', 'SO-WO', 'SR-BR', 'SR-CM', 'SR-CR', 'SR-MA', - 'SR-NI', 'SR-PM', 'SR-PR', 'SR-SA', 'SR-SI', 'SR-WA', 'ST-P', 'ST-S', 'SV-AH', - 'SV-CA', 'SV-CH', 'SV-CU', 'SV-LI', 'SV-MO', 'SV-PA', 'SV-SA', 'SV-SM', 'SV-SO', - 'SV-SS', 'SV-SV', 'SV-UN', 'SV-US', 'SY-DI', 'SY-DR', 'SY-DY', 'SY-HA', 'SY-HI', - 'SY-HL', 'SY-HM', 'SY-ID', 'SY-LA', 'SY-QU', 'SY-RA', 'SY-RD', 'SY-SU', 'SY-TA', - 'SZ-HH', 'SZ-LU', 'SZ-MA', 'SZ-SH', 'TD-BA', 'TD-BET', 'TD-BI', 'TD-CB', 'TD-GR', - 'TD-KA', 'TD-LC', 'TD-LO', 'TD-LR', 'TD-MC', 'TD-MK', 'TD-OD', 'TD-SA', 'TD-TA', - 'TG-C', 'TG-K', 'TG-M', 'TG-P', 'TG-S', 'TH-10', 'TH-11', 'TH-12', 'TH-13', - 'TH-14', 'TH-15', 'TH-16', 'TH-17', 'TH-18', 'TH-19', 'TH-20', 'TH-21', 'TH-22', - 'TH-23', 'TH-24', 'TH-25', 'TH-26', 'TH-27', 'TH-30', 'TH-31', 'TH-32', 'TH-33', - 'TH-34', 'TH-35', 'TH-36', 'TH-37', 'TH-39', 'TH-40', 'TH-41', 'TH-42', 'TH-43', - 'TH-44', 'TH-45', 'TH-46', 'TH-47', 'TH-48', 'TH-49', 'TH-50', 'TH-51', 'TH-52', - 'TH-53', 'TH-54', 'TH-55', 'TH-56', 'TH-57', 'TH-58', 'TH-60', 'TH-61', 'TH-62', - 'TH-63', 'TH-64', 'TH-65', 'TH-66', 'TH-67', 'TH-70', 'TH-71', 'TH-72', 'TH-73', - 'TH-74', 'TH-75', 'TH-76', 'TH-77', 'TH-80', 'TH-81', 'TH-82', 'TH-83', 'TH-84', - 'TH-85', 'TH-86', 'TH-90', 'TH-91', 'TH-92', 'TH-93', 'TH-94', 'TH-95', 'TH-96', - 'TH-S', 'TJ-GB', 'TJ-KT', 'TJ-SU', 'TL-AL', 'TL-AN', 'TL-BA', 'TL-BO', 'TL-CO', - 'TL-DI', 'TL-ER', 'TL-LA', 'TL-LI', 'TL-MF', 'TL-MT', 'TL-OE', 'TL-VI', 'TM-A', - 'TM-B', 'TM-D', 'TM-L', 'TM-M', 'TN-11', 'TN-12', 'TN-13', 'TN-21', 'TN-22', - 'TN-23', 'TN-31', 'TN-32', 'TN-33', 'TN-34', 'TN-41', 'TN-42', 'TN-43', 'TN-51', - 'TN-52', 'TN-53', 'TN-61', 'TN-71', 'TN-72', 'TN-73', 'TN-81', 'TN-82', 'TN-83', - 'TR-01', 'TR-02', 'TR-03', 'TR-04', 'TR-05', 'TR-06', 'TR-07', 'TR-08', 'TR-09', - 'TR-10', 'TR-11', 'TR-12', 'TR-13', 'TR-14', 'TR-15', 'TR-16', 'TR-17', 'TR-18', - 'TR-19', 'TR-20', 'TR-21', 'TR-22', 'TR-23', 'TR-24', 'TR-25', 'TR-26', 'TR-27', - 'TR-28', 'TR-29', 'TR-30', 'TR-31', 'TR-32', 'TR-33', 'TR-34', 'TR-35', 'TR-36', - 'TR-37', 'TR-38', 'TR-39', 'TR-40', 'TR-41', 'TR-42', 'TR-43', 'TR-44', 'TR-45', - 'TR-46', 'TR-47', 'TR-48', 'TR-49', 'TR-50', 'TR-51', 'TR-52', 'TR-53', 'TR-54', - 'TR-55', 'TR-56', 'TR-57', 'TR-58', 'TR-59', 'TR-60', 'TR-61', 'TR-62', 'TR-63', - 'TR-64', 'TR-65', 'TR-66', 'TR-67', 'TR-68', 'TR-69', 'TR-70', 'TR-71', 'TR-72', - 'TR-73', 'TR-74', 'TR-75', 'TR-76', 'TR-77', 'TR-78', 'TR-79', 'TR-80', 'TR-81', - 'TT-ARI', 'TT-CHA', 'TT-CTT', 'TT-DMN', 'TT-ETO', 'TT-PED', 'TT-POS', 'TT-PRT', - 'TT-PTF', 'TT-RCM', 'TT-SFO', 'TT-SGE', 'TT-SIP', 'TT-SJL', 'TT-TUP', 'TT-WTO', - 'TW-CHA', 'TW-CYQ', 'TW-HSQ', 'TW-HUA', 'TW-ILA', 'TW-KEE', 'TW-KHQ', 'TW-MIA', - 'TW-NAN', 'TW-PEN', 'TW-PIF', 'TW-TAO', 'TW-TNQ', 'TW-TPQ', 'TW-TTT', 'TW-TXQ', - 'TW-YUN', 'TZ-01', 'TZ-02', 'TZ-03', 'TZ-04', 'TZ-05', 'TZ-06', 'TZ-07', 'TZ-08', - 'TZ-09', 'TZ-10', 'TZ-11', 'TZ-12', 'TZ-13', 'TZ-14', 'TZ-15', 'TZ-16', 'TZ-17', - 'TZ-18', 'TZ-19', 'TZ-20', 'TZ-21', 'TZ-22', 'TZ-23', 'TZ-24', 'TZ-25', 'UA-05', - 'UA-07', 'UA-09', 'UA-12', 'UA-14', 'UA-18', 'UA-21', 'UA-23', 'UA-26', 'UA-30', - 'UA-32', 'UA-35', 'UA-40', 'UA-43', 'UA-46', 'UA-48', 'UA-51', 'UA-53', 'UA-56', - 'UA-59', 'UA-61', 'UA-63', 'UA-65', 'UA-68', 'UA-71', 'UA-74', 'UA-77', 'UG-AJM', - 'UG-APA', 'UG-ARU', 'UG-BUA', 'UG-BUG', 'UG-BUN', 'UG-BUS', 'UG-C', 'UG-E', - 'UG-GUL', 'UG-HOI', 'UG-IGA', 'UG-JIN', 'UG-KAP', 'UG-KAS', 'UG-KAT', 'UG-KBL', - 'UG-KBR', 'UG-KIB', 'UG-KIS', 'UG-KIT', 'UG-KLA', 'UG-KLE', 'UG-KLG', 'UG-KLI', - 'UG-KOT', 'UG-KUM', 'UG-LIR', 'UG-LUW', 'UG-MBL', 'UG-MBR', 'UG-MOR', 'UG-MOY', - 'UG-MPI', 'UG-MSI', 'UG-MSK', 'UG-MUB', 'UG-MUK', 'UG-N', 'UG-NAK', 'UG-NEB', - 'UG-NTU', 'UG-PAL', 'UG-RAK', 'UG-RUK', 'UG-SEM', 'UG-SOR', 'UG-TOR', 'UG-W', - 'UM-67', 'UM-71', 'UM-76', 'UM-79', 'UM-81', 'UM-84', 'UM-86', 'UM-89', 'UM-95', - 'US-AK', 'US-AL', 'US-AR', 'US-AS', 'US-AZ', 'US-CA', 'US-CO', 'US-CT', 'US-DC', - 'US-DE', 'US-FL', 'US-GA', 'US-GU', 'US-HI', 'US-IA', 'US-ID', 'US-IL', 'US-IN', - 'US-KS', 'US-KY', 'US-LA', 'US-MA', 'US-MD', 'US-ME', 'US-MI', 'US-MN', 'US-MO', - 'US-MP', 'US-MS', 'US-MT', 'US-NC', 'US-ND', 'US-NE', 'US-NH', 'US-NJ', 'US-NM', - 'US-NV', 'US-NY', 'US-OH', 'US-OK', 'US-OR', 'US-PA', 'US-PR', 'US-RI', 'US-SC', - 'US-SD', 'US-TN', 'US-TX', 'US-UM', 'US-UT', 'US-VA', 'US-VI', 'US-VT', 'US-WA', - 'US-WI', 'US-WV', 'US-WY', 'UY-AR', 'UY-CA', 'UY-CL', 'UY-CO', 'UY-DU', 'UY-FD', - 'UY-FS', 'UY-LA', 'UY-MA', 'UY-MO', 'UY-PA', 'UY-RN', 'UY-RO', 'UY-RV', 'UY-SA', - 'UY-SJ', 'UY-SO', 'UY-TA', 'UY-TT', 'UZ-AN', 'UZ-BU', 'UZ-FA', 'UZ-JI', 'UZ-NG', - 'UZ-NW', 'UZ-QA', 'UZ-QR', 'UZ-SA', 'UZ-SI', 'UZ-SU', 'UZ-TK', 'UZ-TO', 'UZ-XO', - 'VE-A', 'VE-B', 'VE-C', 'VE-D', 'VE-E', 'VE-F', 'VE-G', 'VE-H', 'VE-I', 'VE-J', - 'VE-K', 'VE-L', 'VE-M', 'VE-N', 'VE-O', 'VE-P', 'VE-R', 'VE-S', 'VE-T', 'VE-U', - 'VE-V', 'VE-W', 'VE-X', 'VE-Y', 'VE-Z', 'VN-01', 'VN-02', 'VN-03', 'VN-04', - 'VN-05', 'VN-06', 'VN-07', 'VN-09', 'VN-13', 'VN-14', 'VN-15', 'VN-18', 'VN-20', - 'VN-21', 'VN-22', 'VN-23', 'VN-24', 'VN-25', 'VN-26', 'VN-27', 'VN-28', 'VN-29', - 'VN-30', 'VN-31', 'VN-32', 'VN-33', 'VN-34', 'VN-35', 'VN-36', 'VN-37', 'VN-39', - 'VN-40', 'VN-41', 'VN-43', 'VN-44', 'VN-45', 'VN-46', 'VN-47', 'VN-48', 'VN-49', - 'VN-50', 'VN-51', 'VN-52', 'VN-53', 'VN-54', 'VN-55', 'VN-56', 'VN-57', 'VN-58', - 'VN-59', 'VN-60', 'VN-61', 'VN-62', 'VN-63', 'VN-64', 'VN-65', 'VN-66', 'VN-67', - 'VN-68', 'VN-69', 'VN-70', 'VU-MAP', 'VU-PAM', 'VU-SAM', 'VU-SEE', 'VU-TAE', - 'VU-TOB', 'WS-AA', 'WS-AL', 'WS-AT', 'WS-FA', 'WS-GE', 'WS-GI', 'WS-PA', 'WS-SA', - 'WS-TU', 'WS-VF', 'WS-VS', 'YE-AB', 'YE-AD', 'YE-AM', 'YE-BA', 'YE-DA', 'YE-DH', - 'YE-HD', 'YE-HJ', 'YE-HU', 'YE-IB', 'YE-JA', 'YE-LA', 'YE-MA', 'YE-MR', 'YE-MW', - 'YE-SD', 'YE-SH', 'YE-SN', 'YE-TA', 'YU-CG', 'YU-KM', 'YU-SR', 'YU-VO', 'ZA-EC', - 'ZA-FS', 'ZA-GT', 'ZA-MP', 'ZA-NC', 'ZA-NL', 'ZA-NP', 'ZA-NW', 'ZA-WC', 'ZM-01', - 'ZM-02', 'ZM-03', 'ZM-04', 'ZM-05', 'ZM-06', 'ZM-07', 'ZM-08', 'ZM-09', 'ZW-BU', - 'ZW-HA', 'ZW-MA', 'ZW-MC', 'ZW-ME', 'ZW-MI', 'ZW-MN', 'ZW-MS', 'ZW-MV', 'ZW-MW', - name='subdivision'), nullable=False), - sa.Column('city', sa.Unicode(length=32), nullable=False), - sa.Column('city_confidence', sa.SmallInteger(), nullable=False), - sa.Column('isp', sa.Unicode(length=32), nullable=False), - sa.Column('organization', sa.Unicode(length=32), nullable=True), - sa.Column('organization_type', sa.Unicode(length=32), nullable=True), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action_with_one_device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'live', + sa.Column( + 'ip', + teal.db.IP(), + nullable=False, + comment='The IP where the live was triggered.', + ), + sa.Column('subdivision_confidence', sa.SmallInteger(), nullable=False), + sa.Column( + 'subdivision', + sa.Enum( + 'AE-AJ', + 'AE-AZ', + 'AE-DU', + 'AE-FU', + 'AE-RK', + 'AE-SH', + 'AE-UQ', + 'AF-BAL', + 'AF-BAM', + 'AF-BDG', + 'AF-BDS', + 'AF-BGL', + 'AF-FRAU', + 'AF-FYB', + 'AF-GHA', + 'AF-GHO', + 'AF-HEL', + 'AF-HER', + 'AF-JOW', + 'AF-KAB', + 'AF-KANN', + 'AF-KAP', + 'AF-KDZ', + 'AF-KNR', + 'AF-LAG', + 'AF-LOW', + 'AF-NAN', + 'AF-NIM', + 'AF-ORU', + 'AF-PAR', + 'AF-PIA', + 'AF-PKA', + 'AF-SAM', + 'AF-SAR', + 'AF-TAK', + 'AF-WAR', + 'AF-ZAB', + 'AL-BR', + 'AL-BU', + 'AL-DI', + 'AL-DL', + 'AL-DR', + 'AL-DV', + 'AL-EL', + 'AL-ER', + 'AL-FR', + 'AL-GJ', + 'AL-GR', + 'AL-HA', + 'AL-KA', + 'AL-KB', + 'AL-KC', + 'AL-KO', + 'AL-KR', + 'AL-KU', + 'AL-LA', + 'AL-LB', + 'AL-LE', + 'AL-LU', + 'AL-MK', + 'AL-MM', + 'AL-MR', + 'AL-MT', + 'AL-PG', + 'AL-PQ', + 'AL-PR', + 'AL-PU', + 'AL-SH', + 'AL-SK', + 'AL-SR', + 'AL-TE', + 'AL-TP', + 'AL-TR', + 'AL-VL', + 'AM-AG', + 'AM-AR', + 'AM-AV', + 'AM-ER', + 'AM-GR', + 'AM-KT', + 'AM-LO', + 'AM-SH', + 'AM-SU', + 'AM-TV', + 'AM-VD', + 'AO-BGO', + 'AO-BGU', + 'AO-BIE', + 'AO-CAB', + 'AO-CCU', + 'AO-CNN', + 'AO-CNO', + 'AO-CUS', + 'AO-HUA', + 'AO-HUI', + 'AO-LNO', + 'AO-LSU', + 'AO-LUA', + 'AO-MAL', + 'AO-MOX', + 'AO-NAM', + 'AO-UIG', + 'AO-ZAI', + 'AR-A', + 'AR-B', + 'AR-C', + 'AR-D', + 'AR-E', + 'AR-F', + 'AR-G', + 'AR-H', + 'AR-J', + 'AR-K', + 'AR-L', + 'AR-M', + 'AR-N', + 'AR-P', + 'AR-Q', + 'AR-R', + 'AR-S', + 'AR-T', + 'AR-U', + 'AR-V', + 'AR-W', + 'AR-X', + 'AR-Y', + 'AR-Z', + 'AT-1', + 'AT-2', + 'AT-3', + 'AT-4', + 'AT-5', + 'AT-6', + 'AT-7', + 'AT-8', + 'AT-9', + 'AU-CT', + 'AU-NS', + 'AU-NT', + 'AU-QL', + 'AU-SA', + 'AU-TS', + 'AU-VI', + 'AU-WA', + 'AZ-AB', + 'AZ-ABS', + 'AZ-AGA', + 'AZ-AGC', + 'AZ-AGM', + 'AZ-AGS', + 'AZ-AGU', + 'AZ-AST', + 'AZ-BA', + 'AZ-BAB', + 'AZ-BAL', + 'AZ-BAR', + 'AZ-BEY', + 'AZ-BIL', + 'AZ-CAB', + 'AZ-CAL', + 'AZ-CUL', + 'AZ-DAS', + 'AZ-DAV', + 'AZ-FUZ', + 'AZ-GA', + 'AZ-GAD', + 'AZ-GOR', + 'AZ-GOY', + 'AZ-HAC', + 'AZ-IMI', + 'AZ-ISM', + 'AZ-KAL', + 'AZ-KUR', + 'AZ-LA', + 'AZ-LAC', + 'AZ-LAN', + 'AZ-LER', + 'AZ-MAS', + 'AZ-MI', + 'AZ-MM', + 'AZ-NA', + 'AZ-NEF', + 'AZ-OGU', + 'AZ-ORD', + 'AZ-QAB', + 'AZ-QAX', + 'AZ-QAZ', + 'AZ-QBA', + 'AZ-QBI', + 'AZ-QOB', + 'AZ-QUS', + 'AZ-SA', + 'AZ-SAB', + 'AZ-SAD', + 'AZ-SAH', + 'AZ-SAK', + 'AZ-SAL', + 'AZ-SAR', + 'AZ-SAT', + 'AZ-SIY', + 'AZ-SKR', + 'AZ-SM', + 'AZ-SMI', + 'AZ-SMX', + 'AZ-SS', + 'AZ-SUS', + 'AZ-TAR', + 'AZ-TOV', + 'AZ-UCA', + 'AZ-XA', + 'AZ-XAC', + 'AZ-XAN', + 'AZ-XCI', + 'AZ-XIZ', + 'AZ-XVD', + 'AZ-YAR', + 'AZ-YE', + 'AZ-YEV', + 'AZ-ZAN', + 'AZ-ZAQ', + 'AZ-ZAR', + 'BA-BIH', + 'BA-SRP', + 'BD-01', + 'BD-02', + 'BD-03', + 'BD-04', + 'BD-05', + 'BD-06', + 'BD-07', + 'BD-08', + 'BD-09', + 'BD-1', + 'BD-10', + 'BD-11', + 'BD-12', + 'BD-13', + 'BD-14', + 'BD-15', + 'BD-16', + 'BD-17', + 'BD-18', + 'BD-19', + 'BD-2', + 'BD-20', + 'BD-21', + 'BD-22', + 'BD-23', + 'BD-24', + 'BD-25', + 'BD-26', + 'BD-27', + 'BD-28', + 'BD-29', + 'BD-3', + 'BD-30', + 'BD-31', + 'BD-32', + 'BD-33', + 'BD-34', + 'BD-35', + 'BD-36', + 'BD-37', + 'BD-38', + 'BD-39', + 'BD-4', + 'BD-40', + 'BD-41', + 'BD-42', + 'BD-43', + 'BD-44', + 'BD-45', + 'BD-46', + 'BD-47', + 'BD-48', + 'BD-49', + 'BD-5', + 'BD-50', + 'BD-51', + 'BD-52', + 'BD-53', + 'BD-54', + 'BD-55', + 'BD-56', + 'BD-57', + 'BD-58', + 'BD-59', + 'BD-6', + 'BD-60', + 'BD-61', + 'BD-62', + 'BD-63', + 'BD-64', + 'BE-BRU', + 'BE-VAN', + 'BE-VBR', + 'BE-VLG', + 'BE-VLI', + 'BE-VOV', + 'BE-VWV', + 'BE-WAL', + 'BE-WBR', + 'BE-WHT', + 'BE-WLG', + 'BE-WLX', + 'BE-WNA', + 'BF-BAL', + 'BF-BAM', + 'BF-BAN', + 'BF-BAZ', + 'BF-BGR', + 'BF-BLG', + 'BF-BLK', + 'BF-COM', + 'BF-GAN', + 'BF-GNA', + 'BF-GOU', + 'BF-HOU', + 'BF-IOB', + 'BF-KAD', + 'BF-KEN', + 'BF-KMD', + 'BF-KMP', + 'BF-KOP', + 'BF-KOS', + 'BF-KOT', + 'BF-KOW', + 'BF-LER', + 'BF-LOR', + 'BF-MOU', + 'BF-NAM', + 'BF-NAO', + 'BF-NAY', + 'BF-NOU', + 'BF-OUB', + 'BF-OUD', + 'BF-PAS', + 'BF-PON', + 'BF-SEN', + 'BF-SIS', + 'BF-SMT', + 'BF-SNG', + 'BF-SOM', + 'BF-SOR', + 'BF-TAP', + 'BF-TUI', + 'BF-YAG', + 'BF-YAT', + 'BF-ZIR', + 'BF-ZON', + 'BF-ZOU', + 'BG-01', + 'BG-02', + 'BG-03', + 'BG-04', + 'BG-05', + 'BG-06', + 'BG-07', + 'BG-08', + 'BG-09', + 'BG-10', + 'BG-11', + 'BG-12', + 'BG-13', + 'BG-14', + 'BG-15', + 'BG-16', + 'BG-17', + 'BG-18', + 'BG-19', + 'BG-20', + 'BG-21', + 'BG-22', + 'BG-23', + 'BG-24', + 'BG-25', + 'BG-26', + 'BG-27', + 'BG-28', + 'BH-01', + 'BH-02', + 'BH-03', + 'BH-04', + 'BH-05', + 'BH-06', + 'BH-07', + 'BH-08', + 'BH-09', + 'BH-10', + 'BH-11', + 'BH-12', + 'BI-BB', + 'BI-BJ', + 'BI-BR', + 'BI-CA', + 'BI-CI', + 'BI-GI', + 'BI-KI', + 'BI-KR', + 'BI-KY', + 'BI-MA', + 'BI-MU', + 'BI-MW', + 'BI-MY', + 'BI-NG', + 'BI-RT', + 'BI-RY', + 'BJ-AK', + 'BJ-AL', + 'BJ-AQ', + 'BJ-BO', + 'BJ-CO', + 'BJ-DO', + 'BJ-KO', + 'BJ-LI', + 'BJ-MO', + 'BJ-OU', + 'BJ-PL', + 'BJ-ZO', + 'BN-BE', + 'BN-BM', + 'BN-TE', + 'BN-TU', + 'BO-B', + 'BO-C', + 'BO-H', + 'BO-L', + 'BO-N', + 'BO-O', + 'BO-P', + 'BO-S', + 'BO-T', + 'BR-AC', + 'BR-AL', + 'BR-AM', + 'BR-AP', + 'BR-BA', + 'BR-CE', + 'BR-DF', + 'BR-ES', + 'BR-GO', + 'BR-MA', + 'BR-MG', + 'BR-MS', + 'BR-MT', + 'BR-PA', + 'BR-PB', + 'BR-PE', + 'BR-PI', + 'BR-PR', + 'BR-RJ', + 'BR-RN', + 'BR-RO', + 'BR-RR', + 'BR-RS', + 'BR-SC', + 'BR-SE', + 'BR-SP', + 'BR-TO', + 'BS-AC', + 'BS-BI', + 'BS-CI', + 'BS-EX', + 'BS-FC', + 'BS-FP', + 'BS-GH', + 'BS-GT', + 'BS-HI', + 'BS-HR', + 'BS-IN', + 'BS-KB', + 'BS-LI', + 'BS-MG', + 'BS-MH', + 'BS-NB', + 'BS-NP', + 'BS-RI', + 'BS-RS', + 'BS-SP', + 'BS-SR', + 'BT-11', + 'BT-12', + 'BT-13', + 'BT-14', + 'BT-15', + 'BT-21', + 'BT-22', + 'BT-23', + 'BT-24', + 'BT-31', + 'BT-32', + 'BT-33', + 'BT-34', + 'BT-41', + 'BT-42', + 'BT-43', + 'BT-44', + 'BT-45', + 'BT-GA', + 'BT-TY', + 'BW-CE', + 'BW-CH', + 'BW-GH', + 'BW-KG', + 'BW-KL', + 'BW-KW', + 'BW-NE', + 'BW-NG', + 'BW-SE', + 'BW-SO', + 'BY-BR', + 'BY-HO', + 'BY-HR', + 'BY-MA', + 'BY-MI', + 'BY-VI', + 'BZ-BZ', + 'BZ-CY', + 'BZ-CZL', + 'BZ-OW', + 'BZ-SC', + 'BZ-TOL', + 'CA-AB', + 'CA-BC', + 'CA-MB', + 'CA-NB', + 'CA-NL', + 'CA-NS', + 'CA-NT', + 'CA-NU', + 'CA-ON', + 'CA-PE', + 'CA-QC', + 'CA-SK', + 'CA-YT', + 'CD-BC', + 'CD-BN', + 'CD-EQ', + 'CD-KA', + 'CD-KE', + 'CD-KN', + 'CD-KW', + 'CD-MA', + 'CD-NK', + 'CD-OR', + 'CD-SK', + 'CF-AC', + 'CF-BB', + 'CF-BGF', + 'CF-BK', + 'CF-HK', + 'CF-HM', + 'CF-HS', + 'CF-KB', + 'CF-KG', + 'CF-LB', + 'CF-MB', + 'CF-MP', + 'CF-NM', + 'CF-OP', + 'CF-SE', + 'CF-UK', + 'CF-VK', + 'CG-11', + 'CG-12', + 'CG-13', + 'CG-14', + 'CG-15', + 'CG-2', + 'CG-5', + 'CG-7', + 'CG-8', + 'CG-9', + 'CG-BZV', + 'CH-AG', + 'CH-AI', + 'CH-AR', + 'CH-BE', + 'CH-BL', + 'CH-BS', + 'CH-FR', + 'CH-GE', + 'CH-GL', + 'CH-GR', + 'CH-JU', + 'CH-LU', + 'CH-NE', + 'CH-NW', + 'CH-OW', + 'CH-SG', + 'CH-SH', + 'CH-SO', + 'CH-SZ', + 'CH-TG', + 'CH-TI', + 'CH-UR', + 'CH-VD', + 'CH-VS', + 'CH-ZG', + 'CH-ZH', + 'CI-01', + 'CI-02', + 'CI-03', + 'CI-04', + 'CI-05', + 'CI-06', + 'CI-07', + 'CI-08', + 'CI-09', + 'CI-10', + 'CI-11', + 'CI-12', + 'CI-13', + 'CI-14', + 'CI-15', + 'CI-16', + 'CL-AI', + 'CL-AN', + 'CL-AR', + 'CL-AT', + 'CL-BI', + 'CL-CO', + 'CL-LI', + 'CL-LL', + 'CL-MA', + 'CL-ML', + 'CL-RM', + 'CL-TA', + 'CL-VS', + 'CM-AD', + 'CM-CE', + 'CM-EN', + 'CM-ES', + 'CM-LT', + 'CM-NO', + 'CM-NW', + 'CM-OU', + 'CM-SU', + 'CM-SW', + 'CN-11', + 'CN-12', + 'CN-13', + 'CN-14', + 'CN-15', + 'CN-21', + 'CN-22', + 'CN-23', + 'CN-31', + 'CN-32', + 'CN-33', + 'CN-34', + 'CN-35', + 'CN-36', + 'CN-37', + 'CN-41', + 'CN-42', + 'CN-43', + 'CN-44', + 'CN-45', + 'CN-46', + 'CN-50', + 'CN-51', + 'CN-52', + 'CN-53', + 'CN-54', + 'CN-61', + 'CN-62', + 'CN-63', + 'CN-64', + 'CN-65', + 'CN-71', + 'CN-91', + 'CN-92', + 'CO-AMA', + 'CO-ANT', + 'CO-ARA', + 'CO-ATL', + 'CO-BOL', + 'CO-BOY', + 'CO-CAL', + 'CO-CAQ', + 'CO-CAS', + 'CO-CAU', + 'CO-CES', + 'CO-CHO', + 'CO-COR', + 'CO-CUN', + 'CO-DC', + 'CO-GUA', + 'CO-GUV', + 'CO-HUI', + 'CO-LAG', + 'CO-MAG', + 'CO-MET', + 'CO-NAR', + 'CO-NSA', + 'CO-PUT', + 'CO-QUI', + 'CO-RIS', + 'CO-SAN', + 'CO-SAP', + 'CO-SUC', + 'CO-TOL', + 'CO-VAC', + 'CO-VAU', + 'CO-VID', + 'CR-A', + 'CR-C', + 'CR-G', + 'CR-H', + 'CR-L', + 'CR-P', + 'CR-SJ', + 'CU-01', + 'CU-02', + 'CU-03', + 'CU-04', + 'CU-05', + 'CU-06', + 'CU-07', + 'CU-08', + 'CU-09', + 'CU-10', + 'CU-11', + 'CU-12', + 'CU-13', + 'CU-14', + 'CU-99', + 'CV-B', + 'CV-BR', + 'CV-BV', + 'CV-CA', + 'CV-CR', + 'CV-CS', + 'CV-FO', + 'CV-MA', + 'CV-MO', + 'CV-PA', + 'CV-PN', + 'CV-PR', + 'CV-RG', + 'CV-S', + 'CV-SF', + 'CV-SL', + 'CV-SN', + 'CV-SV', + 'CV-TA', + 'CY-01', + 'CY-02', + 'CY-03', + 'CY-04', + 'CY-05', + 'CY-06', + 'CZ-JC', + 'CZ-JM', + 'CZ-KA', + 'CZ-KR', + 'CZ-LI', + 'CZ-MO', + 'CZ-OL', + 'CZ-PA', + 'CZ-PL', + 'CZ-PR', + 'CZ-ST', + 'CZ-US', + 'CZ-VY', + 'CZ-ZL', + 'DE-BB', + 'DE-BE', + 'DE-BW', + 'DE-BY', + 'DE-HB', + 'DE-HE', + 'DE-HH', + 'DE-MV', + 'DE-NI', + 'DE-NW', + 'DE-RP', + 'DE-SH', + 'DE-SL', + 'DE-SN', + 'DE-ST', + 'DE-TH', + 'DJ-AS', + 'DJ-DI', + 'DJ-DJ', + 'DJ-OB', + 'DJ-TA', + 'DK-015', + 'DK-020', + 'DK-025', + 'DK-030', + 'DK-035', + 'DK-040', + 'DK-042', + 'DK-050', + 'DK-055', + 'DK-060', + 'DK-065', + 'DK-070', + 'DK-076', + 'DK-080', + 'DK-101', + 'DK-147', + 'DO-01', + 'DO-02', + 'DO-03', + 'DO-04', + 'DO-05', + 'DO-06', + 'DO-07', + 'DO-08', + 'DO-09', + 'DO-10', + 'DO-11', + 'DO-12', + 'DO-13', + 'DO-14', + 'DO-15', + 'DO-16', + 'DO-17', + 'DO-18', + 'DO-19', + 'DO-20', + 'DO-21', + 'DO-22', + 'DO-23', + 'DO-24', + 'DO-25', + 'DO-26', + 'DO-27', + 'DO-28', + 'DO-29', + 'DO-30', + 'DZ-01', + 'DZ-02', + 'DZ-03', + 'DZ-04', + 'DZ-05', + 'DZ-06', + 'DZ-07', + 'DZ-08', + 'DZ-09', + 'DZ-10', + 'DZ-11', + 'DZ-12', + 'DZ-13', + 'DZ-14', + 'DZ-15', + 'DZ-16', + 'DZ-17', + 'DZ-18', + 'DZ-19', + 'DZ-20', + 'DZ-21', + 'DZ-22', + 'DZ-23', + 'DZ-24', + 'DZ-25', + 'DZ-26', + 'DZ-27', + 'DZ-28', + 'DZ-29', + 'DZ-30', + 'DZ-31', + 'DZ-32', + 'DZ-33', + 'DZ-34', + 'DZ-35', + 'DZ-36', + 'DZ-37', + 'DZ-38', + 'DZ-39', + 'DZ-40', + 'DZ-41', + 'DZ-42', + 'DZ-43', + 'DZ-44', + 'DZ-45', + 'DZ-46', + 'DZ-47', + 'DZ-48', + 'EC-A', + 'EC-B', + 'EC-C', + 'EC-D', + 'EC-E', + 'EC-F', + 'EC-G', + 'EC-H', + 'EC-I', + 'EC-L', + 'EC-M', + 'EC-N', + 'EC-O', + 'EC-P', + 'EC-R', + 'EC-S', + 'EC-T', + 'EC-U', + 'EC-W', + 'EC-X', + 'EC-Y', + 'EC-Z', + 'EE-37', + 'EE-39', + 'EE-44', + 'EE-49', + 'EE-51', + 'EE-57', + 'EE-59', + 'EE-65', + 'EE-67', + 'EE-70', + 'EE-74', + 'EE-78', + 'EE-82', + 'EE-84', + 'EE-86', + 'EG-ALX', + 'EG-ASN', + 'EG-AST', + 'EG-BA', + 'EG-BH', + 'EG-BNS', + 'EG-C', + 'EG-DK', + 'EG-DT', + 'EG-FYM', + 'EG-GH', + 'EG-GZ', + 'EG-IS', + 'EG-JS', + 'EG-KB', + 'EG-KFS', + 'EG-KN', + 'EG-MN', + 'EG-MNF', + 'EG-MT', + 'EG-PTS', + 'EG-SHG', + 'EG-SHR', + 'EG-SIN', + 'EG-SUZ', + 'EG-WAD', + 'ER-AN', + 'ER-DK', + 'ER-DU', + 'ER-GB', + 'ER-MA', + 'ER-SK', + 'ES-A', + 'ES-AB', + 'ES-AL', + 'ES-AN', + 'ES-AR', + 'ES-AV', + 'ES-B', + 'ES-BA', + 'ES-BI', + 'ES-BU', + 'ES-C', + 'ES-CA', + 'ES-CC', + 'ES-CE', + 'ES-CL', + 'ES-CM', + 'ES-CN', + 'ES-CO', + 'ES-CR', + 'ES-CS', + 'ES-CT', + 'ES-CU', + 'ES-EX', + 'ES-GA', + 'ES-GC', + 'ES-GI', + 'ES-GR', + 'ES-GU', + 'ES-H', + 'ES-HU', + 'ES-J', + 'ES-L', + 'ES-LE', + 'ES-LO', + 'ES-LU', + 'ES-M', + 'ES-MA', + 'ES-ML', + 'ES-MU', + 'ES-NA', + 'ES-O', + 'ES-OR', + 'ES-P', + 'ES-PM', + 'ES-PO', + 'ES-PV', + 'ES-S', + 'ES-SA', + 'ES-SE', + 'ES-SG', + 'ES-SO', + 'ES-SS', + 'ES-T', + 'ES-TE', + 'ES-TF', + 'ES-TO', + 'ES-V', + 'ES-VA', + 'ES-VC', + 'ES-VI', + 'ES-Z', + 'ES-ZA', + 'ET-AA', + 'ET-AF', + 'ET-AM', + 'ET-BE', + 'ET-DD', + 'ET-GA', + 'ET-HA', + 'ET-OR', + 'ET-SN', + 'ET-SO', + 'ET-TI', + 'FI-AL', + 'FI-ES', + 'FI-IS', + 'FI-LL', + 'FI-LS', + 'FI-OL', + 'FJ-C', + 'FJ-E', + 'FJ-N', + 'FJ-R', + 'FJ-W', + 'FM-KSA', + 'FM-PNI', + 'FM-TRK', + 'FM-YAP', + 'FR-01', + 'FR-02', + 'FR-03', + 'FR-04', + 'FR-05', + 'FR-06', + 'FR-07', + 'FR-08', + 'FR-09', + 'FR-10', + 'FR-11', + 'FR-12', + 'FR-13', + 'FR-14', + 'FR-15', + 'FR-16', + 'FR-17', + 'FR-18', + 'FR-19', + 'FR-21', + 'FR-22', + 'FR-23', + 'FR-24', + 'FR-25', + 'FR-26', + 'FR-27', + 'FR-28', + 'FR-29', + 'FR-2A', + 'FR-2B', + 'FR-30', + 'FR-31', + 'FR-32', + 'FR-33', + 'FR-34', + 'FR-35', + 'FR-36', + 'FR-37', + 'FR-38', + 'FR-39', + 'FR-40', + 'FR-41', + 'FR-42', + 'FR-43', + 'FR-44', + 'FR-45', + 'FR-46', + 'FR-47', + 'FR-48', + 'FR-49', + 'FR-50', + 'FR-51', + 'FR-52', + 'FR-53', + 'FR-54', + 'FR-55', + 'FR-56', + 'FR-57', + 'FR-58', + 'FR-59', + 'FR-60', + 'FR-61', + 'FR-62', + 'FR-63', + 'FR-64', + 'FR-65', + 'FR-66', + 'FR-67', + 'FR-68', + 'FR-69', + 'FR-70', + 'FR-71', + 'FR-72', + 'FR-73', + 'FR-74', + 'FR-75', + 'FR-76', + 'FR-77', + 'FR-78', + 'FR-79', + 'FR-80', + 'FR-81', + 'FR-82', + 'FR-83', + 'FR-84', + 'FR-85', + 'FR-86', + 'FR-87', + 'FR-88', + 'FR-89', + 'FR-90', + 'FR-91', + 'FR-92', + 'FR-93', + 'FR-94', + 'FR-95', + 'FR-A', + 'FR-B', + 'FR-C', + 'FR-D', + 'FR-E', + 'FR-F', + 'FR-G', + 'FR-GF', + 'FR-GP', + 'FR-H', + 'FR-I', + 'FR-J', + 'FR-K', + 'FR-L', + 'FR-M', + 'FR-MQ', + 'FR-N', + 'FR-NC', + 'FR-O', + 'FR-P', + 'FR-PF', + 'FR-PM', + 'FR-Q', + 'FR-R', + 'FR-RE', + 'FR-S', + 'FR-T', + 'FR-TF', + 'FR-U', + 'FR-V', + 'FR-WF', + 'FR-YT', + 'GA-1', + 'GA-2', + 'GA-3', + 'GA-4', + 'GA-5', + 'GA-6', + 'GA-7', + 'GA-8', + 'GA-9', + 'GB-ABD', + 'GB-ABE', + 'GB-AGB', + 'GB-AGY', + 'GB-ANS', + 'GB-ANT', + 'GB-ARD', + 'GB-ARM', + 'GB-BAS', + 'GB-BBD', + 'GB-BDF', + 'GB-BDG', + 'GB-BEN', + 'GB-BEX', + 'GB-BFS', + 'GB-BGE', + 'GB-BGW', + 'GB-BIR', + 'GB-BKM', + 'GB-BLA', + 'GB-BLY', + 'GB-BMH', + 'GB-BNB', + 'GB-BNE', + 'GB-BNH', + 'GB-BNS', + 'GB-BOL', + 'GB-BPL', + 'GB-BRC', + 'GB-BRD', + 'GB-BRY', + 'GB-BST', + 'GB-BUR', + 'GB-CAM', + 'GB-CAY', + 'GB-CGN', + 'GB-CGV', + 'GB-CHA', + 'GB-CHS', + 'GB-CKF', + 'GB-CKT', + 'GB-CLD', + 'GB-CLK', + 'GB-CLR', + 'GB-CMA', + 'GB-CMD', + 'GB-CMN', + 'GB-CON', + 'GB-COV', + 'GB-CRF', + 'GB-CRY', + 'GB-CSR', + 'GB-CWY', + 'GB-DAL', + 'GB-DBY', + 'GB-DEN', + 'GB-DER', + 'GB-DEV', + 'GB-DGN', + 'GB-DGY', + 'GB-DNC', + 'GB-DND', + 'GB-DOR', + 'GB-DOW', + 'GB-DRY', + 'GB-DUD', + 'GB-DUR', + 'GB-EAL', + 'GB-EAW', + 'GB-EAY', + 'GB-EDH', + 'GB-EDU', + 'GB-ELN', + 'GB-ELS', + 'GB-ENF', + 'GB-ENG', + 'GB-ERW', + 'GB-ERY', + 'GB-ESS', + 'GB-ESX', + 'GB-FAL', + 'GB-FER', + 'GB-FIF', + 'GB-FLN', + 'GB-GAT', + 'GB-GBN', + 'GB-GLG', + 'GB-GLS', + 'GB-GRE', + 'GB-GSY', + 'GB-GWN', + 'GB-HAL', + 'GB-HAM', + 'GB-HAV', + 'GB-HCK', + 'GB-HEF', + 'GB-HIL', + 'GB-HLD', + 'GB-HMF', + 'GB-HNS', + 'GB-HPL', + 'GB-HRT', + 'GB-HRW', + 'GB-HRY', + 'GB-IOM', + 'GB-IOS', + 'GB-IOW', + 'GB-ISL', + 'GB-IVC', + 'GB-JSY', + 'GB-KEC', + 'GB-KEN', + 'GB-KHL', + 'GB-KIR', + 'GB-KTT', + 'GB-KWL', + 'GB-LAN', + 'GB-LBH', + 'GB-LCE', + 'GB-LDS', + 'GB-LEC', + 'GB-LEW', + 'GB-LIN', + 'GB-LIV', + 'GB-LMV', + 'GB-LND', + 'GB-LRN', + 'GB-LSB', + 'GB-LUT', + 'GB-MAN', + 'GB-MDB', + 'GB-MDW', + 'GB-MFT', + 'GB-MIK', + 'GB-MLN', + 'GB-MON', + 'GB-MRT', + 'GB-MRY', + 'GB-MTY', + 'GB-MYL', + 'GB-NAY', + 'GB-NBL', + 'GB-NDN', + 'GB-NEL', + 'GB-NET', + 'GB-NFK', + 'GB-NGM', + 'GB-NIR', + 'GB-NLK', + 'GB-NLN', + 'GB-NSM', + 'GB-NTA', + 'GB-NTH', + 'GB-NTL', + 'GB-NTT', + 'GB-NTY', + 'GB-NWM', + 'GB-NWP', + 'GB-NYK', + 'GB-NYM', + 'GB-OLD', + 'GB-OMH', + 'GB-ORK', + 'GB-OXF', + 'GB-PEM', + 'GB-PKN', + 'GB-PLY', + 'GB-POL', + 'GB-POR', + 'GB-POW', + 'GB-PTE', + 'GB-RCC', + 'GB-RCH', + 'GB-RCT', + 'GB-RDB', + 'GB-RDG', + 'GB-RFW', + 'GB-RIC', + 'GB-ROT', + 'GB-RUT', + 'GB-SAW', + 'GB-SAY', + 'GB-SCB', + 'GB-SCT', + 'GB-SFK', + 'GB-SFT', + 'GB-SGC', + 'GB-SHF', + 'GB-SHN', + 'GB-SHR', + 'GB-SKP', + 'GB-SLF', + 'GB-SLG', + 'GB-SLK', + 'GB-SND', + 'GB-SOL', + 'GB-SOM', + 'GB-SOS', + 'GB-SRY', + 'GB-STB', + 'GB-STE', + 'GB-STG', + 'GB-STH', + 'GB-STN', + 'GB-STS', + 'GB-STT', + 'GB-STY', + 'GB-SWA', + 'GB-SWD', + 'GB-SWK', + 'GB-TAM', + 'GB-TFW', + 'GB-THR', + 'GB-TOB', + 'GB-TOF', + 'GB-TRF', + 'GB-TWH', + 'GB-UKM', + 'GB-VGL', + 'GB-WAR', + 'GB-WBK', + 'GB-WDU', + 'GB-WFT', + 'GB-WGN', + 'GB-WILL', + 'GB-WKF', + 'GB-WLL', + 'GB-WLN', + 'GB-WLS', + 'GB-WLV', + 'GB-WND', + 'GB-WNM', + 'GB-WOK', + 'GB-WOR', + 'GB-WRL', + 'GB-WRT', + 'GB-WRX', + 'GB-WSM', + 'GB-WSX', + 'GB-YOR', + 'GB-ZET', + 'GE-AB', + 'GE-AJ', + 'GE-GU', + 'GE-IM', + 'GE-KA', + 'GE-KK', + 'GE-MM', + 'GE-RL', + 'GE-SJ', + 'GE-SK', + 'GE-SZ', + 'GE-TB', + 'GH-AA', + 'GH-AH', + 'GH-BA', + 'GH-CP', + 'GH-EP', + 'GH-NP', + 'GH-TV', + 'GH-UE', + 'GH-UW', + 'GH-WP', + 'GM-B', + 'GM-L', + 'GM-M', + 'GM-N', + 'GM-U', + 'GM-W', + 'GN-B', + 'GN-BE', + 'GN-BF', + 'GN-BK', + 'GN-C', + 'GN-CO', + 'GN-D', + 'GN-DB', + 'GN-DI', + 'GN-DL', + 'GN-DU', + 'GN-F', + 'GN-FA', + 'GN-FO', + 'GN-FR', + 'GN-GA', + 'GN-GU', + 'GN-K', + 'GN-KA', + 'GN-KB', + 'GN-KD; 2', + 'GN-KE', + 'GN-KN', + 'GN-KO', + 'GN-KS', + 'GN-L', + 'GN-LA', + 'GN-LE', + 'GN-LO', + 'GN-M', + 'GN-MC', + 'GN-MD', + 'GN-ML', + 'GN-MM', + 'GN-N', + 'GN-NZ', + 'GN-PI', + 'GN-SI', + 'GN-TE', + 'GN-TO', + 'GN-YO', + 'GQ-AN', + 'GQ-BN', + 'GQ-BS', + 'GQ-C', + 'GQ-CS', + 'GQ-I', + 'GQ-KN', + 'GQ-LI', + 'GQ-WN', + 'GR-01', + 'GR-03', + 'GR-04', + 'GR-05', + 'GR-06', + 'GR-07', + 'GR-11', + 'GR-12', + 'GR-13', + 'GR-14', + 'GR-15', + 'GR-16', + 'GR-17', + 'GR-21', + 'GR-22', + 'GR-23', + 'GR-24', + 'GR-31', + 'GR-32', + 'GR-33', + 'GR-34', + 'GR-41', + 'GR-42', + 'GR-43', + 'GR-44', + 'GR-51', + 'GR-52', + 'GR-53', + 'GR-54', + 'GR-55', + 'GR-56', + 'GR-57', + 'GR-58', + 'GR-59', + 'GR-61', + 'GR-62', + 'GR-63', + 'GR-64', + 'GR-69', + 'GR-71', + 'GR-72', + 'GR-73', + 'GR-81', + 'GR-82', + 'GR-83', + 'GR-84', + 'GR-85', + 'GR-91', + 'GR-92', + 'GR-93', + 'GR-94', + 'GR-A1', + 'GR-I', + 'GR-II', + 'GR-III', + 'GR-IV', + 'GR-IX', + 'GR-V', + 'GR-VI', + 'GR-VII', + 'GR-VIII', + 'GR-X', + 'GR-XI', + 'GR-XII', + 'GR-XIII', + 'GT-AV', + 'GT-BV', + 'GT-CM', + 'GT-CQ', + 'GT-ES', + 'GT-GU', + 'GT-HU', + 'GT-IZ', + 'GT-JA', + 'GT-JU', + 'GT-PE', + 'GT-PR', + 'GT-QC', + 'GT-QZ', + 'GT-RE', + 'GT-SA', + 'GT-SM', + 'GT-SO', + 'GT-SR', + 'GT-SU', + 'GT-TO', + 'GT-ZA', + 'GW-BA', + 'GW-BL', + 'GW-BM', + 'GW-BS', + 'GW-CA', + 'GW-GA', + 'GW-L', + 'GW-N', + 'GW-OI', + 'GW-QU', + 'GW-S', + 'GW-TO', + 'GY-BA', + 'GY-CU', + 'GY-DE', + 'GY-EB', + 'GY-ES', + 'GY-MA', + 'GY-PM', + 'GY-PT', + 'GY-UD', + 'GY-UT', + 'HN-AT', + 'HN-CH', + 'HN-CL', + 'HN-CM', + 'HN-CP', + 'HN-CR', + 'HN-EP', + 'HN-FM', + 'HN-GD', + 'HN-IB', + 'HN-IN', + 'HN-LE', + 'HN-LP', + 'HN-OC', + 'HN-OL', + 'HN-SB', + 'HN-VA', + 'HN-YO', + 'HR-01', + 'HR-02', + 'HR-03', + 'HR-04', + 'HR-05', + 'HR-06', + 'HR-07', + 'HR-08', + 'HR-09', + 'HR-10', + 'HR-11', + 'HR-12', + 'HR-13', + 'HR-14', + 'HR-15', + 'HR-16', + 'HR-17', + 'HR-18', + 'HR-19', + 'HR-20', + 'HR-21', + 'HT-AR', + 'HT-CE', + 'HT-GA', + 'HT-ND', + 'HT-NE', + 'HT-NO', + 'HT-OU', + 'HT-SD', + 'HT-SE', + 'HU-BA', + 'HU-BC', + 'HU-BE', + 'HU-BK', + 'HU-BU', + 'HU-BZ', + 'HU-CS', + 'HU-DE', + 'HU-DU', + 'HU-EG', + 'HU-FE', + 'HU-GS', + 'HU-GY', + 'HU-HB', + 'HU-HE', + 'HU-HV', + 'HU-JN', + 'HU-KE', + 'HU-KM', + 'HU-KV', + 'HU-MI', + 'HU-NK', + 'HU-NO', + 'HU-NY', + 'HU-PE', + 'HU-PS', + 'HU-SD', + 'HU-SF', + 'HU-SH', + 'HU-SK', + 'HU-SN', + 'HU-SO', + 'HU-SS', + 'HU-ST', + 'HU-SZ', + 'HU-TB', + 'HU-TO', + 'HU-VA', + 'HU-VE', + 'HU-VM', + 'HU-ZA', + 'HU-ZE', + 'ID-AC', + 'ID-BA', + 'ID-BB', + 'ID-BE', + 'ID-BT', + 'ID-GO', + 'ID-IJ', + 'ID-JA', + 'ID-JB', + 'ID-JI', + 'ID-JK', + 'ID-JT', + 'ID-JW', + 'ID-KA', + 'ID-KB', + 'ID-KI', + 'ID-KS', + 'ID-KT', + 'ID-LA', + 'ID-MA', + 'ID-MU', + 'ID-NB', + 'ID-NT', + 'ID-NU', + 'ID-PA', + 'ID-RI', + 'ID-SA', + 'ID-SB', + 'ID-SG', + 'ID-SL', + 'ID-SM', + 'ID-SN', + 'ID-SS', + 'ID-ST', + 'ID-SU', + 'ID-YO', + 'IE-C', + 'IE-C; 2', + 'IE-CE', + 'IE-CN', + 'IE-CW', + 'IE-D', + 'IE-DL', + 'IE-G', + 'IE-KE', + 'IE-KK', + 'IE-KY', + 'IE-L', + 'IE-LD', + 'IE-LH', + 'IE-LK', + 'IE-LM', + 'IE-LS', + 'IE-M', + 'IE-MH', + 'IE-MN', + 'IE-MO', + 'IE-OY', + 'IE-RN', + 'IE-SO', + 'IE-TA', + 'IE-U', + 'IE-WD', + 'IE-WH', + 'IE-WW', + 'IE-WX', + 'IL-D', + 'IL-HA', + 'IL-JM', + 'IL-M', + 'IL-TA', + 'IL-Z', + 'IN-AN', + 'IN-AP', + 'IN-AR', + 'IN-AS', + 'IN-BR', + 'IN-CH', + 'IN-CT', + 'IN-DD', + 'IN-DL', + 'IN-DN', + 'IN-GA', + 'IN-GJ', + 'IN-HP', + 'IN-HR', + 'IN-JH', + 'IN-JK', + 'IN-KA', + 'IN-KL', + 'IN-LD', + 'IN-MH', + 'IN-ML', + 'IN-MN', + 'IN-MP', + 'IN-MZ', + 'IN-NL', + 'IN-OR', + 'IN-PB', + 'IN-PY', + 'IN-RJ', + 'IN-SK', + 'IN-TN', + 'IN-TR', + 'IN-UL', + 'IN-UP', + 'IN-WB', + 'IQ-AN', + 'IQ-AR', + 'IQ-BA', + 'IQ-BB', + 'IQ-BG', + 'IQ-DA', + 'IQ-DI', + 'IQ-DQ', + 'IQ-KA', + 'IQ-MA', + 'IQ-MU', + 'IQ-NA', + 'IQ-NI', + 'IQ-QA', + 'IQ-SD', + 'IQ-SU', + 'IQ-TS', + 'IQ-WA', + 'IR-01', + 'IR-02', + 'IR-03', + 'IR-04', + 'IR-05', + 'IR-06', + 'IR-07', + 'IR-08', + 'IR-09', + 'IR-10', + 'IR-11', + 'IR-12', + 'IR-13', + 'IR-14', + 'IR-15', + 'IR-16', + 'IR-17', + 'IR-18', + 'IR-19', + 'IR-20', + 'IR-21', + 'IR-22', + 'IR-23', + 'IR-24', + 'IR-25', + 'IR-26', + 'IR-27', + 'IR-28', + 'IS-0', + 'IS-1', + 'IS-2', + 'IS-3', + 'IS-4', + 'IS-5', + 'IS-6', + 'IS-7', + 'IS-8', + 'IT-21', + 'IT-23', + 'IT-25', + 'IT-32', + 'IT-34', + 'IT-36', + 'IT-42', + 'IT-45', + 'IT-52', + 'IT-55', + 'IT-57', + 'IT-62', + 'IT-65', + 'IT-67', + 'IT-72', + 'IT-75', + 'IT-77', + 'IT-78', + 'IT-82', + 'IT-88', + 'IT-AG', + 'IT-AL', + 'IT-AN', + 'IT-AO', + 'IT-AP', + 'IT-AQ', + 'IT-AR', + 'IT-AT', + 'IT-AV', + 'IT-BA', + 'IT-BG', + 'IT-BI', + 'IT-BL', + 'IT-BN', + 'IT-BO', + 'IT-BR', + 'IT-BS', + 'IT-BZ', + 'IT-CA', + 'IT-CB', + 'IT-CE', + 'IT-CH', + 'IT-CL', + 'IT-CN', + 'IT-CO', + 'IT-CR', + 'IT-CS', + 'IT-CT', + 'IT-CZ', + 'IT-DU', + 'IT-EN', + 'IT-FE', + 'IT-FG', + 'IT-FI', + 'IT-FO', + 'IT-FR', + 'IT-GE', + 'IT-GO', + 'IT-GR', + 'IT-IM', + 'IT-IS', + 'IT-KR', + 'IT-LC', + 'IT-LE', + 'IT-LI', + 'IT-LO', + 'IT-LT', + 'IT-LU', + 'IT-MC', + 'IT-ME', + 'IT-MI', + 'IT-MN', + 'IT-MO', + 'IT-MS', + 'IT-MT', + 'IT-NA', + 'IT-NO', + 'IT-NU', + 'IT-OR', + 'IT-PA', + 'IT-PC', + 'IT-PD', + 'IT-PE', + 'IT-PG', + 'IT-PI', + 'IT-PN', + 'IT-PO', + 'IT-PR', + 'IT-PS', + 'IT-PT', + 'IT-PV', + 'IT-PZ', + 'IT-RA', + 'IT-RC', + 'IT-RE', + 'IT-RG', + 'IT-RI', + 'IT-RM', + 'IT-RN', + 'IT-RO', + 'IT-SA', + 'IT-SI', + 'IT-SO', + 'IT-SP', + 'IT-SR', + 'IT-SS', + 'IT-SV', + 'IT-TA', + 'IT-TE', + 'IT-TN', + 'IT-TO', + 'IT-TP', + 'IT-TR', + 'IT-TS', + 'IT-TV', + 'IT-VA', + 'IT-VB', + 'IT-VC', + 'IT-VE', + 'IT-VI', + 'IT-VR', + 'IT-VT', + 'IT-VV', + 'JM-01', + 'JM-02', + 'JM-03', + 'JM-04', + 'JM-05', + 'JM-06', + 'JM-07', + 'JM-08', + 'JM-09', + 'JM-10', + 'JM-11', + 'JM-12', + 'JM-13', + 'JM-14', + 'JO-AJ', + 'JO-AM', + 'JO-AQ', + 'JO-AT', + 'JO-AZ', + 'JO-BA', + 'JO-IR', + 'JO-JA', + 'JO-KA', + 'JO-MA', + 'JO-MD', + 'JO-MN', + 'JP-01', + 'JP-02', + 'JP-03', + 'JP-04', + 'JP-05', + 'JP-06', + 'JP-07', + 'JP-08', + 'JP-09', + 'JP-10', + 'JP-11', + 'JP-12', + 'JP-13', + 'JP-14', + 'JP-15', + 'JP-16', + 'JP-17', + 'JP-18', + 'JP-19', + 'JP-20', + 'JP-21', + 'JP-22', + 'JP-23', + 'JP-24', + 'JP-25', + 'JP-26', + 'JP-27', + 'JP-28', + 'JP-29', + 'JP-30', + 'JP-31', + 'JP-32', + 'JP-33', + 'JP-34', + 'JP-35', + 'JP-36', + 'JP-37', + 'JP-38', + 'JP-39', + 'JP-40', + 'JP-41', + 'JP-42', + 'JP-43', + 'JP-44', + 'JP-45', + 'JP-46', + 'JP-47', + 'KE-110', + 'KE-200', + 'KE-300', + 'KE-400', + 'KE-500', + 'KE-600', + 'KE-700', + 'KE-900', + 'KG-B', + 'KG-C', + 'KG-GB', + 'KG-J', + 'KG-N', + 'KG-O', + 'KG-T', + 'KG-Y', + 'KH-1', + 'KH-10', + 'KH-11', + 'KH-12', + 'KH-13', + 'KH-14', + 'KH-15', + 'KH-16', + 'KH-17', + 'KH-18', + 'KH-19', + 'KH-2', + 'KH-20', + 'KH-21', + 'KH-22', + 'KH-23', + 'KH-24', + 'KH-3', + 'KH-4', + 'KH-5', + 'KH-6', + 'KH-7', + 'KH-8', + 'KH-9', + 'KI-G', + 'KI-L', + 'KI-P', + 'KM-A', + 'KM-G', + 'KM-M', + 'KP-CHA', + 'KP-HAB', + 'KP-HAN', + 'KP-HWB', + 'KP-HWN', + 'KP-KAE', + 'KP-KAN', + 'KP-NAJ', + 'KP-NAM', + 'KP-PYB', + 'KP-PYN', + 'KP-PYO', + 'KP-YAN', + 'KR-11', + 'KR-26', + 'KR-27', + 'KR-28', + 'KR-29', + 'KR-30', + 'KR-31', + 'KR-41', + 'KR-42', + 'KR-43', + 'KR-44', + 'KR-45', + 'KR-46', + 'KR-47', + 'KR-48', + 'KR-49', + 'KW-AH', + 'KW-FA', + 'KW-HA', + 'KW-JA', + 'KW-KU', + 'KZ-AKM', + 'KZ-AKT', + 'KZ-ALA', + 'KZ-ALM', + 'KZ-AST', + 'KZ-ATY', + 'KZ-KAR', + 'KZ-KUS', + 'KZ-KZY', + 'KZ-MAN', + 'KZ-PAV', + 'KZ-SEV', + 'KZ-VOS', + 'KZ-YUZ', + 'KZ-ZAP', + 'KZ-ZHA', + 'LA-AT', + 'LA-BK', + 'LA-BL', + 'LA-CH', + 'LA-HO', + 'LA-KH', + 'LA-LM', + 'LA-LP', + 'LA-OU', + 'LA-PH', + 'LA-SL', + 'LA-SV', + 'LA-VI', + 'LA-VT', + 'LA-XA', + 'LA-XE', + 'LA-XI', + 'LA-XN', + 'LB-AS', + 'LB-BA', + 'LB-BI', + 'LB-JA', + 'LB-JL', + 'LB-NA', + 'LK-1', + 'LK-11', + 'LK-12', + 'LK-13', + 'LK-2', + 'LK-21', + 'LK-22', + 'LK-23', + 'LK-3', + 'LK-31', + 'LK-32', + 'LK-33', + 'LK-4', + 'LK-41', + 'LK-42', + 'LK-43', + 'LK-44', + 'LK-45', + 'LK-5', + 'LK-51', + 'LK-52', + 'LK-53', + 'LK-6', + 'LK-61', + 'LK-62', + 'LK-7', + 'LK-71', + 'LK-72', + 'LK-8', + 'LK-81', + 'LK-82', + 'LK-9', + 'LK-91', + 'LK-92', + 'LR-BG', + 'LR-BM', + 'LR-CM', + 'LR-GB', + 'LR-GG', + 'LR-GK', + 'LR-LO', + 'LR-MG', + 'LR-MO', + 'LR-MY', + 'LR-NI', + 'LR-RI', + 'LR-SI', + 'LS-A', + 'LS-B', + 'LS-C', + 'LS-D', + 'LS-E', + 'LS-F', + 'LS-G', + 'LS-H', + 'LS-J', + 'LS-K', + 'LT-AL', + 'LT-KL', + 'LT-KU', + 'LT-MR', + 'LT-PN', + 'LT-SA', + 'LT-TA', + 'LT-TE', + 'LT-UT', + 'LT-VL', + 'LU-D', + 'LU-G', + 'LU-L', + 'LV-AI', + 'LV-AL', + 'LV-BL', + 'LV-BU', + 'LV-CE', + 'LV-DA', + 'LV-DGV', + 'LV-DO', + 'LV-GU', + 'LV-JEL', + 'LV-JK', + 'LV-JL', + 'LV-JUR', + 'LV-KR', + 'LV-KU', + 'LV-LE', + 'LV-LM', + 'LV-LPX', + 'LV-LU', + 'LV-MA', + 'LV-OG', + 'LV-PR', + 'LV-RE', + 'LV-REZ', + 'LV-RI', + 'LV-RIX', + 'LV-SA', + 'LV-TA', + 'LV-TU', + 'LV-VE', + 'LV-VEN', + 'LV-VK', + 'LV-VM', + 'LY-BA', + 'LY-BU', + 'LY-FA', + 'LY-JA', + 'LY-JG', + 'LY-JU', + 'LY-MI', + 'LY-NA', + 'LY-SF', + 'LY-TB', + 'LY-WA', + 'LY-WU', + 'LY-ZA', + 'MA-01', + 'MA-02', + 'MA-03', + 'MA-04', + 'MA-05', + 'MA-06', + 'MA-07', + 'MA-08', + 'MA-09', + 'MA-10', + 'MA-11', + 'MA-12', + 'MA-13', + 'MA-14', + 'MA-15', + 'MA-16', + 'MA-AGD', + 'MA-ASZ', + 'MA-AZI', + 'MA-BAH', + 'MA-BEM', + 'MA-BER', + 'MA-BES', + 'MA-BOD', + 'MA-BOM', + 'MA-CAS', + 'MA-CHE', + 'MA-CHI', + 'MA-ERR', + 'MA-ESI', + 'MA-ESM', + 'MA-FES', + 'MA-FIG', + 'MA-GUE', + 'MA-HAJ', + 'MA-HAO', + 'MA-HOC', + 'MA-IFR', + 'MA-JDI', + 'MA-JRA', + 'MA-KEN', + 'MA-KES', + 'MA-KHE', + 'MA-KHN', + 'MA-KHO', + 'MA-LAA', + 'MA-LAR', + 'MA-MAR', + 'MA-MEK', + 'MA-MEL', + 'MA-NAD', + 'MA-OUA', + 'MA-OUD', + 'MA-OUJ', + 'MA-RBA', + 'MA-SAF', + 'MA-SEF', + 'MA-SET', + 'MA-SIK', + 'MA-TAO', + 'MA-TAR', + 'MA-TAT', + 'MA-TAZ', + 'MA-TET', + 'MA-TIZ', + 'MA-TNG', + 'MA-TNT', + 'MD-BA', + 'MD-CA', + 'MD-CH', + 'MD-CU', + 'MD-ED', + 'MD-GA', + 'MD-LA', + 'MD-OR', + 'MD-SN', + 'MD-SO', + 'MD-TA', + 'MD-TI', + 'MD-UN', + 'MG-A', + 'MG-D', + 'MG-F', + 'MG-M', + 'MG-T', + 'MG-U', + 'MH-ALK', + 'MH-ALL', + 'MH-ARN', + 'MH-AUR', + 'MH-EBO', + 'MH-ENI', + 'MH-JAL', + 'MH-KIL', + 'MH-KWA', + 'MH-L', + 'MH-LAE', + 'MH-LIB', + 'MH-LIK', + 'MH-MAJ', + 'MH-MAL', + 'MH-MEJ', + 'MH-MIL', + 'MH-NMK', + 'MH-NMU', + 'MH-RON', + 'MH-T', + 'MH-UJA', + 'MH-UJL', + 'MH-UTI', + 'MH-WTH', + 'MH-WTJ', + 'ML-1', + 'ML-2', + 'ML-3', + 'ML-4', + 'ML-5', + 'ML-6', + 'ML-7', + 'ML-8', + 'ML-BKO', + 'MM-01', + 'MM-02', + 'MM-03', + 'MM-04', + 'MM-05', + 'MM-06', + 'MM-07', + 'MM-11', + 'MM-12', + 'MM-13', + 'MM-14', + 'MM-15', + 'MM-16', + 'MM-17', + 'MN-035', + 'MN-037', + 'MN-039', + 'MN-041', + 'MN-043', + 'MN-046', + 'MN-047', + 'MN-049', + 'MN-051', + 'MN-053', + 'MN-055', + 'MN-057', + 'MN-059', + 'MN-061', + 'MN-063', + 'MN-064', + 'MN-065', + 'MN-067', + 'MN-069', + 'MN-071', + 'MN-073', + 'MN-1', + 'MR-01', + 'MR-02', + 'MR-03', + 'MR-04', + 'MR-05', + 'MR-06', + 'MR-07', + 'MR-08', + 'MR-09', + 'MR-10', + 'MR-11', + 'MR-12', + 'MR-NKC', + 'MU-AG', + 'MU-BL', + 'MU-BR', + 'MU-CC', + 'MU-CU', + 'MU-FL', + 'MU-GP', + 'MU-MO', + 'MU-PA', + 'MU-PL', + 'MU-PU', + 'MU-PW', + 'MU-QB', + 'MU-RO', + 'MU-RR', + 'MU-SA', + 'MU-VP', + 'MV-01', + 'MV-02', + 'MV-03', + 'MV-04', + 'MV-05', + 'MV-07', + 'MV-08', + 'MV-12', + 'MV-13', + 'MV-14', + 'MV-17', + 'MV-20', + 'MV-23', + 'MV-24', + 'MV-25', + 'MV-26', + 'MV-27', + 'MV-28', + 'MV-29', + 'MV-MLE', + 'MW-BA', + 'MW-BL', + 'MW-C', + 'MW-CK', + 'MW-CR', + 'MW-CT', + 'MW-DE', + 'MW-DO', + 'MW-KR', + 'MW-KS', + 'MW-LI', + 'MW-LK', + 'MW-MC', + 'MW-MG', + 'MW-MH', + 'MW-MU', + 'MW-MW', + 'MW-MZ', + 'MW-N', + 'MW-NB', + 'MW-NI', + 'MW-NK', + 'MW-NS', + 'MW-NU', + 'MW-PH', + 'MW-RU', + 'MW-S', + 'MW-SA', + 'MW-TH', + 'MW-ZO', + 'MX-AGU', + 'MX-BCN', + 'MX-BCS', + 'MX-CAM', + 'MX-CHH', + 'MX-CHP', + 'MX-COA', + 'MX-COL', + 'MX-DIF', + 'MX-DUR', + 'MX-GRO', + 'MX-GUA', + 'MX-HID', + 'MX-JAL', + 'MX-MEX', + 'MX-MIC', + 'MX-MOR', + 'MX-NAY', + 'MX-NLE', + 'MX-OAX', + 'MX-PUE', + 'MX-QUE', + 'MX-ROO', + 'MX-SIN', + 'MX-SLP', + 'MX-SON', + 'MX-TAB', + 'MX-TAM', + 'MX-TLA', + 'MX-VER', + 'MX-YUC', + 'MX-ZAC', + 'MY-A', + 'MY-B', + 'MY-C', + 'MY-D', + 'MY-J', + 'MY-K', + 'MY-L', + 'MY-M', + 'MY-N', + 'MY-P', + 'MY-R', + 'MY-SA', + 'MY-SK', + 'MY-T', + 'MY-W', + 'MZ-A', + 'MZ-B', + 'MZ-G', + 'MZ-I', + 'MZ-L', + 'MZ-MPM', + 'MZ-N', + 'MZ-P', + 'MZ-Q', + 'MZ-S', + 'MZ-T', + 'NA-CA', + 'NA-ER', + 'NA-HA', + 'NA-KA', + 'NA-KH', + 'NA-KU', + 'NA-OD', + 'NA-OH', + 'NA-OK', + 'NA-ON', + 'NA-OS', + 'NA-OT', + 'NA-OW', + 'NE-1', + 'NE-2', + 'NE-3', + 'NE-4', + 'NE-5', + 'NE-6', + 'NE-7', + 'NE-8', + 'NG-AB', + 'NG-AD', + 'NG-AK', + 'NG-AN', + 'NG-BA', + 'NG-BE', + 'NG-BO', + 'NG-BY', + 'NG-CR', + 'NG-DE', + 'NG-EB', + 'NG-ED', + 'NG-EK', + 'NG-EN', + 'NG-FC', + 'NG-GO', + 'NG-IM', + 'NG-JI', + 'NG-KD', + 'NG-KE', + 'NG-KN', + 'NG-KO', + 'NG-KT', + 'NG-KW', + 'NG-LA', + 'NG-NA', + 'NG-NI', + 'NG-OG', + 'NG-ON', + 'NG-OS', + 'NG-OY', + 'NG-PL', + 'NG-RI', + 'NG-SO', + 'NG-TA', + 'NG-YO', + 'NG-ZA', + 'NI-AN', + 'NI-AS', + 'NI-BO', + 'NI-CA', + 'NI-CI', + 'NI-CO', + 'NI-ES', + 'NI-GR', + 'NI-JI', + 'NI-LE', + 'NI-MD', + 'NI-MN', + 'NI-MS', + 'NI-MT', + 'NI-NS', + 'NI-RI', + 'NI-SJ', + 'NL-DR', + 'NL-FL', + 'NL-FR', + 'NL-GE', + 'NL-GR', + 'NL-LI', + 'NL-NB', + 'NL-NH', + 'NL-OV', + 'NL-UT', + 'NL-ZE', + 'NL-ZH', + 'NO-01', + 'NO-02', + 'NO-03', + 'NO-04', + 'NO-05', + 'NO-06', + 'NO-07', + 'NO-08', + 'NO-09', + 'NO-10', + 'NO-11', + 'NO-12', + 'NO-14', + 'NO-15', + 'NO-16', + 'NO-17', + 'NO-18', + 'NO-19', + 'NO-20', + 'NO-21', + 'NO-22', + 'NP-1', + 'NP-2', + 'NP-3', + 'NP-4', + 'NP-5', + 'NP-BA', + 'NP-BH', + 'NP-DH', + 'NP-GA', + 'NP-JA', + 'NP-KA', + 'NP-KO', + 'NP-LU', + 'NP-MA', + 'NP-ME', + 'NP-NA', + 'NP-RA', + 'NP-SA', + 'NP-SE', + 'NZ-AUK', + 'NZ-BOP', + 'NZ-CAN', + 'NZ-GIS', + 'NZ-HKB', + 'NZ-MBH', + 'NZ-MWT', + 'NZ-N', + 'NZ-NSN', + 'NZ-NTL', + 'NZ-OTA', + 'NZ-S', + 'NZ-STL', + 'NZ-TAS', + 'NZ-TKI', + 'NZ-WGN', + 'NZ-WKO', + 'NZ-WTC', + 'OM-BA', + 'OM-DA', + 'OM-JA', + 'OM-MA', + 'OM-MU', + 'OM-SH', + 'OM-WU', + 'OM-ZA', + 'PA-0', + 'PA-1', + 'PA-2', + 'PA-3', + 'PA-4', + 'PA-5', + 'PA-6', + 'PA-7', + 'PA-8', + 'PA-9', + 'PE-AMA', + 'PE-ANC', + 'PE-APU', + 'PE-ARE', + 'PE-AYA', + 'PE-CAJ', + 'PE-CAL', + 'PE-CUS', + 'PE-HUC', + 'PE-HUV', + 'PE-ICA', + 'PE-JUN', + 'PE-LAL', + 'PE-LAM', + 'PE-LIM', + 'PE-LOR', + 'PE-MDD', + 'PE-MOQ', + 'PE-PAS', + 'PE-PIU', + 'PE-PUN', + 'PE-SAM', + 'PE-TAC', + 'PE-TUM', + 'PE-UCA', + 'PG-CPK', + 'PG-CPM', + 'PG-EBR', + 'PG-EHG', + 'PG-EPW', + 'PG-ESW', + 'PG-GPK', + 'PG-MBA', + 'PG-MPL', + 'PG-MPM', + 'PG-MRL', + 'PG-NCD', + 'PG-NIK', + 'PG-NPP', + 'PG-NSA', + 'PG-SAN', + 'PG-SHM', + 'PG-WBK', + 'PG-WHM', + 'PG-WPD', + 'PH-00', + 'PH-01', + 'PH-02', + 'PH-03', + 'PH-04', + 'PH-05', + 'PH-06', + 'PH-07', + 'PH-08', + 'PH-09', + 'PH-10', + 'PH-11', + 'PH-12', + 'PH-13', + 'PH-14', + 'PH-15', + 'PH-ABR', + 'PH-AGN', + 'PH-AGS', + 'PH-AKL', + 'PH-ALB', + 'PH-ANT', + 'PH-APA', + 'PH-AUR', + 'PH-BAN', + 'PH-BAS', + 'PH-BEN', + 'PH-BIL', + 'PH-BOH', + 'PH-BTG', + 'PH-BTN', + 'PH-BUK', + 'PH-BUL', + 'PH-CAG', + 'PH-CAM', + 'PH-CAN', + 'PH-CAP', + 'PH-CAS', + 'PH-CAT', + 'PH-CAV', + 'PH-CEB', + 'PH-COM', + 'PH-DAO', + 'PH-DAS', + 'PH-DAV', + 'PH-EAS', + 'PH-GUI', + 'PH-IFU', + 'PH-ILI', + 'PH-ILN', + 'PH-ILS', + 'PH-ISA', + 'PH-KAL', + 'PH-LAG', + 'PH-LAN', + 'PH-LAS', + 'PH-LEY', + 'PH-LUN', + 'PH-MAD', + 'PH-MAG', + 'PH-MAS', + 'PH-MDC', + 'PH-MDR', + 'PH-MOU', + 'PH-MSC', + 'PH-MSR', + 'PH-NCO', + 'PH-NEC', + 'PH-NER', + 'PH-NSA', + 'PH-NUE', + 'PH-NUV', + 'PH-PAM', + 'PH-PAN', + 'PH-PLW', + 'PH-QUE', + 'PH-QUI', + 'PH-RIZ', + 'PH-ROM', + 'PH-SAR', + 'PH-SCO', + 'PH-SIG', + 'PH-SLE', + 'PH-SLU', + 'PH-SOR', + 'PH-SUK', + 'PH-SUN', + 'PH-SUR', + 'PH-TAR', + 'PH-TAW', + 'PH-WSA', + 'PH-ZAN', + 'PH-ZAS', + 'PH-ZMB', + 'PH-ZSI', + 'PK-BA', + 'PK-IS', + 'PK-JK', + 'PK-NA', + 'PK-NW', + 'PK-PB', + 'PK-SD', + 'PK-TA', + 'PL-DS', + 'PL-KP', + 'PL-LB', + 'PL-LD', + 'PL-LU', + 'PL-MA', + 'PL-MZ', + 'PL-OP', + 'PL-PD', + 'PL-PK', + 'PL-PM', + 'PL-SK', + 'PL-SL', + 'PL-WN', + 'PL-WP', + 'PL-ZP', + 'PT-01', + 'PT-02', + 'PT-03', + 'PT-04', + 'PT-05', + 'PT-06', + 'PT-07', + 'PT-08', + 'PT-09', + 'PT-10', + 'PT-11', + 'PT-12', + 'PT-13', + 'PT-14', + 'PT-15', + 'PT-16', + 'PT-17', + 'PT-18', + 'PT-20', + 'PT-30', + 'PY-1', + 'PY-10', + 'PY-11', + 'PY-12', + 'PY-13', + 'PY-14', + 'PY-15', + 'PY-16', + 'PY-19', + 'PY-2', + 'PY-3', + 'PY-4', + 'PY-5', + 'PY-6', + 'PY-7', + 'PY-8', + 'PY-9', + 'PY-ASU', + 'QA-DA', + 'QA-GH', + 'QA-JB', + 'QA-JU', + 'QA-KH', + 'QA-MS', + 'QA-RA', + 'QA-US', + 'QA-WA', + 'RO-AB', + 'RO-AG', + 'RO-AR', + 'RO-B', + 'RO-BC', + 'RO-BH', + 'RO-BN', + 'RO-BR', + 'RO-BT', + 'RO-BV', + 'RO-BZ', + 'RO-CJ', + 'RO-CL', + 'RO-CS', + 'RO-CT', + 'RO-CV', + 'RO-DB', + 'RO-DJ', + 'RO-GJ', + 'RO-GL', + 'RO-GR', + 'RO-HD', + 'RO-HR', + 'RO-IF', + 'RO-IL', + 'RO-IS', + 'RO-MH', + 'RO-MM', + 'RO-MS', + 'RO-NT', + 'RO-OT', + 'RO-PH', + 'RO-SB', + 'RO-SJ', + 'RO-SM', + 'RO-SV', + 'RO-TL', + 'RO-TM', + 'RO-TR', + 'RO-VL', + 'RO-VN', + 'RO-VS', + 'RU-AD', + 'RU-AGB', + 'RU-AL', + 'RU-ALT', + 'RU-AMU', + 'RU-ARK', + 'RU-AST', + 'RU-BA', + 'RU-BEL', + 'RU-BRY', + 'RU-BU', + 'RU-CE', + 'RU-CHE', + 'RU-CHI', + 'RU-CHU', + 'RU-CU', + 'RU-DA', + 'RU-DU', + 'RU-EVE', + 'RU-IN', + 'RU-IRK', + 'RU-IVA', + 'RU-KAM', + 'RU-KB', + 'RU-KC', + 'RU-KDA', + 'RU-KEM', + 'RU-KGD', + 'RU-KGN', + 'RU-KHA', + 'RU-KHM', + 'RU-KIR', + 'RU-KK', + 'RU-KL', + 'RU-KLU', + 'RU-KO', + 'RU-KOP', + 'RU-KOR', + 'RU-KOS', + 'RU-KR', + 'RU-KRS', + 'RU-KYA', + 'RU-LEN', + 'RU-LIP', + 'RU-MAG', + 'RU-ME', + 'RU-MO', + 'RU-MOS', + 'RU-MOW', + 'RU-MUR', + 'RU-NEN', + 'RU-NGR', + 'RU-NIZ', + 'RU-NVS', + 'RU-OMS', + 'RU-ORE', + 'RU-ORL', + 'RU-PER', + 'RU-PNZ', + 'RU-PRI', + 'RU-PSK', + 'RU-ROS', + 'RU-RYA', + 'RU-SA', + 'RU-SAK', + 'RU-SAM', + 'RU-SAR', + 'RU-SE', + 'RU-SMO', + 'RU-SPE', + 'RU-STA', + 'RU-SVE', + 'RU-TA', + 'RU-TAM', + 'RU-TAY', + 'RU-TOM', + 'RU-TUL', + 'RU-TVE', + 'RU-TY', + 'RU-TYU', + 'RU-ULY', + 'RU-UOB', + 'RU-VGG', + 'RU-VLA', + 'RU-VLG', + 'RU-VOR', + 'RU-YAN', + 'RU-YAR', + 'RU-YEV', + 'RW-B', + 'RW-C', + 'RW-D', + 'RW-E', + 'RW-F', + 'RW-G', + 'RW-H', + 'RW-I', + 'RW-J', + 'RW-K', + 'RW-L', + 'RW-M', + 'SA-01', + 'SA-02', + 'SA-03', + 'SA-04', + 'SA-05', + 'SA-06', + 'SA-07', + 'SA-08', + 'SA-09', + 'SA-10', + 'SA-11', + 'SA-12', + 'SA-14', + 'SB-CE', + 'SB-CT', + 'SB-GU', + 'SB-IS', + 'SB-MK', + 'SB-ML', + 'SB-TE', + 'SB-WE', + 'SD-01', + 'SD-02', + 'SD-03', + 'SD-04', + 'SD-05', + 'SD-06', + 'SD-07', + 'SD-08', + 'SD-09', + 'SD-10', + 'SD-11', + 'SD-12', + 'SD-13', + 'SD-14', + 'SD-15', + 'SD-16', + 'SD-17', + 'SD-18', + 'SD-19', + 'SD-20', + 'SD-21', + 'SD-22', + 'SD-23', + 'SD-24', + 'SD-25', + 'SD-26', + 'SE-AB', + 'SE-AC', + 'SE-BD', + 'SE-C', + 'SE-D', + 'SE-E', + 'SE-F', + 'SE-G', + 'SE-H', + 'SE-I', + 'SE-K', + 'SE-M', + 'SE-N', + 'SE-O', + 'SE-S', + 'SE-T', + 'SE-U', + 'SE-W', + 'SE-X', + 'SE-Y', + 'SE-Z', + 'SH-AC', + 'SH-SH', + 'SH-TA', + 'SI-01', + 'SI-02', + 'SI-03', + 'SI-04', + 'SI-05', + 'SI-06', + 'SI-07', + 'SI-08', + 'SI-09', + 'SI-10', + 'SI-11', + 'SI-12', + 'SK-BC', + 'SK-BL', + 'SK-KI', + 'SK-NI', + 'SK-PV', + 'SK-TA', + 'SK-TC', + 'SK-ZI', + 'SL-E', + 'SL-N', + 'SL-S', + 'SL-W', + 'SN-DB', + 'SN-DK', + 'SN-FK', + 'SN-KD', + 'SN-KL', + 'SN-LG', + 'SN-SL', + 'SN-TC', + 'SN-TH', + 'SN-ZG', + 'SO-AW', + 'SO-BK', + 'SO-BN', + 'SO-BR', + 'SO-BY', + 'SO-GA', + 'SO-GE', + 'SO-HI', + 'SO-JD', + 'SO-JH', + 'SO-MU', + 'SO-NU', + 'SO-SA', + 'SO-SD', + 'SO-SH', + 'SO-SO', + 'SO-TO', + 'SO-WO', + 'SR-BR', + 'SR-CM', + 'SR-CR', + 'SR-MA', + 'SR-NI', + 'SR-PM', + 'SR-PR', + 'SR-SA', + 'SR-SI', + 'SR-WA', + 'ST-P', + 'ST-S', + 'SV-AH', + 'SV-CA', + 'SV-CH', + 'SV-CU', + 'SV-LI', + 'SV-MO', + 'SV-PA', + 'SV-SA', + 'SV-SM', + 'SV-SO', + 'SV-SS', + 'SV-SV', + 'SV-UN', + 'SV-US', + 'SY-DI', + 'SY-DR', + 'SY-DY', + 'SY-HA', + 'SY-HI', + 'SY-HL', + 'SY-HM', + 'SY-ID', + 'SY-LA', + 'SY-QU', + 'SY-RA', + 'SY-RD', + 'SY-SU', + 'SY-TA', + 'SZ-HH', + 'SZ-LU', + 'SZ-MA', + 'SZ-SH', + 'TD-BA', + 'TD-BET', + 'TD-BI', + 'TD-CB', + 'TD-GR', + 'TD-KA', + 'TD-LC', + 'TD-LO', + 'TD-LR', + 'TD-MC', + 'TD-MK', + 'TD-OD', + 'TD-SA', + 'TD-TA', + 'TG-C', + 'TG-K', + 'TG-M', + 'TG-P', + 'TG-S', + 'TH-10', + 'TH-11', + 'TH-12', + 'TH-13', + 'TH-14', + 'TH-15', + 'TH-16', + 'TH-17', + 'TH-18', + 'TH-19', + 'TH-20', + 'TH-21', + 'TH-22', + 'TH-23', + 'TH-24', + 'TH-25', + 'TH-26', + 'TH-27', + 'TH-30', + 'TH-31', + 'TH-32', + 'TH-33', + 'TH-34', + 'TH-35', + 'TH-36', + 'TH-37', + 'TH-39', + 'TH-40', + 'TH-41', + 'TH-42', + 'TH-43', + 'TH-44', + 'TH-45', + 'TH-46', + 'TH-47', + 'TH-48', + 'TH-49', + 'TH-50', + 'TH-51', + 'TH-52', + 'TH-53', + 'TH-54', + 'TH-55', + 'TH-56', + 'TH-57', + 'TH-58', + 'TH-60', + 'TH-61', + 'TH-62', + 'TH-63', + 'TH-64', + 'TH-65', + 'TH-66', + 'TH-67', + 'TH-70', + 'TH-71', + 'TH-72', + 'TH-73', + 'TH-74', + 'TH-75', + 'TH-76', + 'TH-77', + 'TH-80', + 'TH-81', + 'TH-82', + 'TH-83', + 'TH-84', + 'TH-85', + 'TH-86', + 'TH-90', + 'TH-91', + 'TH-92', + 'TH-93', + 'TH-94', + 'TH-95', + 'TH-96', + 'TH-S', + 'TJ-GB', + 'TJ-KT', + 'TJ-SU', + 'TL-AL', + 'TL-AN', + 'TL-BA', + 'TL-BO', + 'TL-CO', + 'TL-DI', + 'TL-ER', + 'TL-LA', + 'TL-LI', + 'TL-MF', + 'TL-MT', + 'TL-OE', + 'TL-VI', + 'TM-A', + 'TM-B', + 'TM-D', + 'TM-L', + 'TM-M', + 'TN-11', + 'TN-12', + 'TN-13', + 'TN-21', + 'TN-22', + 'TN-23', + 'TN-31', + 'TN-32', + 'TN-33', + 'TN-34', + 'TN-41', + 'TN-42', + 'TN-43', + 'TN-51', + 'TN-52', + 'TN-53', + 'TN-61', + 'TN-71', + 'TN-72', + 'TN-73', + 'TN-81', + 'TN-82', + 'TN-83', + 'TR-01', + 'TR-02', + 'TR-03', + 'TR-04', + 'TR-05', + 'TR-06', + 'TR-07', + 'TR-08', + 'TR-09', + 'TR-10', + 'TR-11', + 'TR-12', + 'TR-13', + 'TR-14', + 'TR-15', + 'TR-16', + 'TR-17', + 'TR-18', + 'TR-19', + 'TR-20', + 'TR-21', + 'TR-22', + 'TR-23', + 'TR-24', + 'TR-25', + 'TR-26', + 'TR-27', + 'TR-28', + 'TR-29', + 'TR-30', + 'TR-31', + 'TR-32', + 'TR-33', + 'TR-34', + 'TR-35', + 'TR-36', + 'TR-37', + 'TR-38', + 'TR-39', + 'TR-40', + 'TR-41', + 'TR-42', + 'TR-43', + 'TR-44', + 'TR-45', + 'TR-46', + 'TR-47', + 'TR-48', + 'TR-49', + 'TR-50', + 'TR-51', + 'TR-52', + 'TR-53', + 'TR-54', + 'TR-55', + 'TR-56', + 'TR-57', + 'TR-58', + 'TR-59', + 'TR-60', + 'TR-61', + 'TR-62', + 'TR-63', + 'TR-64', + 'TR-65', + 'TR-66', + 'TR-67', + 'TR-68', + 'TR-69', + 'TR-70', + 'TR-71', + 'TR-72', + 'TR-73', + 'TR-74', + 'TR-75', + 'TR-76', + 'TR-77', + 'TR-78', + 'TR-79', + 'TR-80', + 'TR-81', + 'TT-ARI', + 'TT-CHA', + 'TT-CTT', + 'TT-DMN', + 'TT-ETO', + 'TT-PED', + 'TT-POS', + 'TT-PRT', + 'TT-PTF', + 'TT-RCM', + 'TT-SFO', + 'TT-SGE', + 'TT-SIP', + 'TT-SJL', + 'TT-TUP', + 'TT-WTO', + 'TW-CHA', + 'TW-CYQ', + 'TW-HSQ', + 'TW-HUA', + 'TW-ILA', + 'TW-KEE', + 'TW-KHQ', + 'TW-MIA', + 'TW-NAN', + 'TW-PEN', + 'TW-PIF', + 'TW-TAO', + 'TW-TNQ', + 'TW-TPQ', + 'TW-TTT', + 'TW-TXQ', + 'TW-YUN', + 'TZ-01', + 'TZ-02', + 'TZ-03', + 'TZ-04', + 'TZ-05', + 'TZ-06', + 'TZ-07', + 'TZ-08', + 'TZ-09', + 'TZ-10', + 'TZ-11', + 'TZ-12', + 'TZ-13', + 'TZ-14', + 'TZ-15', + 'TZ-16', + 'TZ-17', + 'TZ-18', + 'TZ-19', + 'TZ-20', + 'TZ-21', + 'TZ-22', + 'TZ-23', + 'TZ-24', + 'TZ-25', + 'UA-05', + 'UA-07', + 'UA-09', + 'UA-12', + 'UA-14', + 'UA-18', + 'UA-21', + 'UA-23', + 'UA-26', + 'UA-30', + 'UA-32', + 'UA-35', + 'UA-40', + 'UA-43', + 'UA-46', + 'UA-48', + 'UA-51', + 'UA-53', + 'UA-56', + 'UA-59', + 'UA-61', + 'UA-63', + 'UA-65', + 'UA-68', + 'UA-71', + 'UA-74', + 'UA-77', + 'UG-AJM', + 'UG-APA', + 'UG-ARU', + 'UG-BUA', + 'UG-BUG', + 'UG-BUN', + 'UG-BUS', + 'UG-C', + 'UG-E', + 'UG-GUL', + 'UG-HOI', + 'UG-IGA', + 'UG-JIN', + 'UG-KAP', + 'UG-KAS', + 'UG-KAT', + 'UG-KBL', + 'UG-KBR', + 'UG-KIB', + 'UG-KIS', + 'UG-KIT', + 'UG-KLA', + 'UG-KLE', + 'UG-KLG', + 'UG-KLI', + 'UG-KOT', + 'UG-KUM', + 'UG-LIR', + 'UG-LUW', + 'UG-MBL', + 'UG-MBR', + 'UG-MOR', + 'UG-MOY', + 'UG-MPI', + 'UG-MSI', + 'UG-MSK', + 'UG-MUB', + 'UG-MUK', + 'UG-N', + 'UG-NAK', + 'UG-NEB', + 'UG-NTU', + 'UG-PAL', + 'UG-RAK', + 'UG-RUK', + 'UG-SEM', + 'UG-SOR', + 'UG-TOR', + 'UG-W', + 'UM-67', + 'UM-71', + 'UM-76', + 'UM-79', + 'UM-81', + 'UM-84', + 'UM-86', + 'UM-89', + 'UM-95', + 'US-AK', + 'US-AL', + 'US-AR', + 'US-AS', + 'US-AZ', + 'US-CA', + 'US-CO', + 'US-CT', + 'US-DC', + 'US-DE', + 'US-FL', + 'US-GA', + 'US-GU', + 'US-HI', + 'US-IA', + 'US-ID', + 'US-IL', + 'US-IN', + 'US-KS', + 'US-KY', + 'US-LA', + 'US-MA', + 'US-MD', + 'US-ME', + 'US-MI', + 'US-MN', + 'US-MO', + 'US-MP', + 'US-MS', + 'US-MT', + 'US-NC', + 'US-ND', + 'US-NE', + 'US-NH', + 'US-NJ', + 'US-NM', + 'US-NV', + 'US-NY', + 'US-OH', + 'US-OK', + 'US-OR', + 'US-PA', + 'US-PR', + 'US-RI', + 'US-SC', + 'US-SD', + 'US-TN', + 'US-TX', + 'US-UM', + 'US-UT', + 'US-VA', + 'US-VI', + 'US-VT', + 'US-WA', + 'US-WI', + 'US-WV', + 'US-WY', + 'UY-AR', + 'UY-CA', + 'UY-CL', + 'UY-CO', + 'UY-DU', + 'UY-FD', + 'UY-FS', + 'UY-LA', + 'UY-MA', + 'UY-MO', + 'UY-PA', + 'UY-RN', + 'UY-RO', + 'UY-RV', + 'UY-SA', + 'UY-SJ', + 'UY-SO', + 'UY-TA', + 'UY-TT', + 'UZ-AN', + 'UZ-BU', + 'UZ-FA', + 'UZ-JI', + 'UZ-NG', + 'UZ-NW', + 'UZ-QA', + 'UZ-QR', + 'UZ-SA', + 'UZ-SI', + 'UZ-SU', + 'UZ-TK', + 'UZ-TO', + 'UZ-XO', + 'VE-A', + 'VE-B', + 'VE-C', + 'VE-D', + 'VE-E', + 'VE-F', + 'VE-G', + 'VE-H', + 'VE-I', + 'VE-J', + 'VE-K', + 'VE-L', + 'VE-M', + 'VE-N', + 'VE-O', + 'VE-P', + 'VE-R', + 'VE-S', + 'VE-T', + 'VE-U', + 'VE-V', + 'VE-W', + 'VE-X', + 'VE-Y', + 'VE-Z', + 'VN-01', + 'VN-02', + 'VN-03', + 'VN-04', + 'VN-05', + 'VN-06', + 'VN-07', + 'VN-09', + 'VN-13', + 'VN-14', + 'VN-15', + 'VN-18', + 'VN-20', + 'VN-21', + 'VN-22', + 'VN-23', + 'VN-24', + 'VN-25', + 'VN-26', + 'VN-27', + 'VN-28', + 'VN-29', + 'VN-30', + 'VN-31', + 'VN-32', + 'VN-33', + 'VN-34', + 'VN-35', + 'VN-36', + 'VN-37', + 'VN-39', + 'VN-40', + 'VN-41', + 'VN-43', + 'VN-44', + 'VN-45', + 'VN-46', + 'VN-47', + 'VN-48', + 'VN-49', + 'VN-50', + 'VN-51', + 'VN-52', + 'VN-53', + 'VN-54', + 'VN-55', + 'VN-56', + 'VN-57', + 'VN-58', + 'VN-59', + 'VN-60', + 'VN-61', + 'VN-62', + 'VN-63', + 'VN-64', + 'VN-65', + 'VN-66', + 'VN-67', + 'VN-68', + 'VN-69', + 'VN-70', + 'VU-MAP', + 'VU-PAM', + 'VU-SAM', + 'VU-SEE', + 'VU-TAE', + 'VU-TOB', + 'WS-AA', + 'WS-AL', + 'WS-AT', + 'WS-FA', + 'WS-GE', + 'WS-GI', + 'WS-PA', + 'WS-SA', + 'WS-TU', + 'WS-VF', + 'WS-VS', + 'YE-AB', + 'YE-AD', + 'YE-AM', + 'YE-BA', + 'YE-DA', + 'YE-DH', + 'YE-HD', + 'YE-HJ', + 'YE-HU', + 'YE-IB', + 'YE-JA', + 'YE-LA', + 'YE-MA', + 'YE-MR', + 'YE-MW', + 'YE-SD', + 'YE-SH', + 'YE-SN', + 'YE-TA', + 'YU-CG', + 'YU-KM', + 'YU-SR', + 'YU-VO', + 'ZA-EC', + 'ZA-FS', + 'ZA-GT', + 'ZA-MP', + 'ZA-NC', + 'ZA-NL', + 'ZA-NP', + 'ZA-NW', + 'ZA-WC', + 'ZM-01', + 'ZM-02', + 'ZM-03', + 'ZM-04', + 'ZM-05', + 'ZM-06', + 'ZM-07', + 'ZM-08', + 'ZM-09', + 'ZW-BU', + 'ZW-HA', + 'ZW-MA', + 'ZW-MC', + 'ZW-ME', + 'ZW-MI', + 'ZW-MN', + 'ZW-MS', + 'ZW-MV', + 'ZW-MW', + name='subdivision', + ), + nullable=False, + ), + sa.Column('city', sa.Unicode(length=32), nullable=False), + sa.Column('city_confidence', sa.SmallInteger(), nullable=False), + sa.Column('isp', sa.Unicode(length=32), nullable=False), + sa.Column('organization', sa.Unicode(length=32), nullable=True), + sa.Column('organization_type', sa.Unicode(length=32), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action_with_one_device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Rate table - op.create_table('rate', - sa.Column('rating', sa.Float(decimal_return_scale=2), nullable=True, - comment='The rating for the content.'), - sa.Column('version', teal.db.StrictVersionType(), nullable=True, - comment='The version of the software.'), - sa.Column('appearance', sa.Float(decimal_return_scale=2), nullable=True, - comment='Subjective value representing aesthetic aspects.'), - sa.Column('functionality', sa.Float(decimal_return_scale=2), nullable=True, - comment='Subjective value representing usage aspects.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action_with_one_device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'rate', + sa.Column( + 'rating', + sa.Float(decimal_return_scale=2), + nullable=True, + comment='The rating for the content.', + ), + sa.Column( + 'version', + teal.db.StrictVersionType(), + nullable=True, + comment='The version of the software.', + ), + sa.Column( + 'appearance', + sa.Float(decimal_return_scale=2), + nullable=True, + comment='Subjective value representing aesthetic aspects.', + ), + sa.Column( + 'functionality', + sa.Float(decimal_return_scale=2), + nullable=True, + comment='Subjective value representing usage aspects.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action_with_one_device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Snapshot table - op.create_table('snapshot', - sa.Column('uuid', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('version', teal.db.StrictVersionType(length=32), nullable=False), - sa.Column('software', sa.Enum('Workbench', 'WorkbenchAndroid', 'AndroidApp', 'Web', 'DesktopApp', - name='snapshotsoftware'), nullable=False), - sa.Column('elapsed', sa.Interval(), nullable=True, - comment='For Snapshots made with Workbench, the total amount \n of time it took to complete.\n '), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action_with_one_device.id'], ), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('uuid'), - schema=f'{get_inv()}' - ) + op.create_table( + 'snapshot', + sa.Column('uuid', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('version', teal.db.StrictVersionType(length=32), nullable=False), + sa.Column( + 'software', + sa.Enum( + 'Workbench', + 'WorkbenchAndroid', + 'AndroidApp', + 'Web', + 'DesktopApp', + name='snapshotsoftware', + ), + nullable=False, + ), + sa.Column( + 'elapsed', + sa.Interval(), + nullable=True, + comment='For Snapshots made with Workbench, the total amount \n of time it took to complete.\n ', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action_with_one_device.id'], + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('uuid'), + schema=f'{get_inv()}', + ) # Test table - op.create_table('test', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action_with_one_device.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action_with_one_device.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # BenchmarkDataStorage table - op.create_table('benchmark_data_storage', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('read_speed', sa.Float(decimal_return_scale=2), nullable=False), - sa.Column('write_speed', sa.Float(decimal_return_scale=2), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.benchmark.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'benchmark_data_storage', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('read_speed', sa.Float(decimal_return_scale=2), nullable=False), + sa.Column('write_speed', sa.Float(decimal_return_scale=2), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.benchmark.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # BenchmarkWithRate table - op.create_table('benchmark_with_rate', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('rate', sa.Float(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.benchmark.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'benchmark_with_rate', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('rate', sa.Float(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.benchmark.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # MeasureBattery table - op.create_table('measure_battery', - sa.Column('size', sa.Integer(), nullable=False, comment='Maximum battery capacity, in mAh.'), - sa.Column('voltage', sa.Integer(), nullable=False, - comment='The actual voltage of the battery, in mV.'), - sa.Column('cycle_count', sa.Integer(), nullable=True, - comment='The number of full charges – discharges \n cycles.\n '), - sa.Column('health', sa.Enum('Cold', 'Dead', 'Good', 'Overheat', 'OverVoltage', 'UnspecifiedValue', - name='batteryhealth'), nullable=True, - comment='The health of the Battery. \n Only reported in Android.\n '), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'measure_battery', + sa.Column( + 'size', + sa.Integer(), + nullable=False, + comment='Maximum battery capacity, in mAh.', + ), + sa.Column( + 'voltage', + sa.Integer(), + nullable=False, + comment='The actual voltage of the battery, in mV.', + ), + sa.Column( + 'cycle_count', + sa.Integer(), + nullable=True, + comment='The number of full charges – discharges \n cycles.\n ', + ), + sa.Column( + 'health', + sa.Enum( + 'Cold', + 'Dead', + 'Good', + 'Overheat', + 'OverVoltage', + 'UnspecifiedValue', + name='batteryhealth', + ), + nullable=True, + comment='The health of the Battery. \n Only reported in Android.\n ', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Price table - op.create_table('price', - sa.Column('currency', - sa.Enum('AFN', 'ARS', 'AWG', 'AUD', 'AZN', 'BSD', 'BBD', 'BDT', 'BYR', 'BZD', 'BMD', - 'BOB', 'BAM', 'BWP', 'BGN', 'BRL', 'BND', 'KHR', 'CAD', 'KYD', 'CLP', 'CNY', - 'COP', 'CRC', 'HRK', 'CUP', 'CZK', 'DKK', 'DOP', 'XCD', 'EGP', 'SVC', 'EEK', - 'EUR', 'FKP', 'FJD', 'GHC', 'GIP', 'GTQ', 'GGP', 'GYD', 'HNL', 'HKD', 'HUF', - 'ISK', 'INR', 'IDR', 'IRR', 'IMP', 'ILS', 'JMD', 'JPY', 'JEP', 'KZT', 'KPW', - 'KRW', 'KGS', 'LAK', 'LVL', 'LBP', 'LRD', 'LTL', 'MKD', 'MYR', 'MUR', 'MXN', - 'MNT', 'MZN', 'NAD', 'NPR', 'ANG', 'NZD', 'NIO', 'NGN', 'NOK', 'OMR', 'PKR', - 'PAB', 'PYG', 'PEN', 'PHP', 'PLN', 'QAR', 'RON', 'RUB', 'SHP', 'SAR', 'RSD', - 'SCR', 'SGD', 'SBD', 'SOS', 'ZAR', 'LKR', 'SEK', 'CHF', 'SRD', 'SYP', 'TWD', - 'THB', 'TTD', 'TRY', 'TRL', 'TVD', 'UAH', 'GBP', 'USD', 'UYU', 'UZS', 'VEF', - 'VND', 'YER', 'ZWD', name='currency'), nullable=False, - comment='The currency of this price as for ISO 4217.'), - sa.Column('price', sa.Numeric(precision=19, scale=4), nullable=False, comment='The value.'), - sa.Column('software', sa.Enum('Ereuse', name='pricesoftware'), nullable=True, - comment='The software used to compute this price,\n if the price was computed automatically. This field is None\n if the price has been manually set.\n '), - sa.Column('version', teal.db.StrictVersionType(), nullable=True, - comment='The version of the software, or None.'), - sa.Column('rating_id', postgresql.UUID(as_uuid=True), nullable=True, - comment='The Rate used to auto-compute\n this price, if it has not been set manually.\n '), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action_with_one_device.id'], ), - sa.ForeignKeyConstraint(['rating_id'], [f'{get_inv()}.rate.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'price', + sa.Column( + 'currency', + sa.Enum( + 'AFN', + 'ARS', + 'AWG', + 'AUD', + 'AZN', + 'BSD', + 'BBD', + 'BDT', + 'BYR', + 'BZD', + 'BMD', + 'BOB', + 'BAM', + 'BWP', + 'BGN', + 'BRL', + 'BND', + 'KHR', + 'CAD', + 'KYD', + 'CLP', + 'CNY', + 'COP', + 'CRC', + 'HRK', + 'CUP', + 'CZK', + 'DKK', + 'DOP', + 'XCD', + 'EGP', + 'SVC', + 'EEK', + 'EUR', + 'FKP', + 'FJD', + 'GHC', + 'GIP', + 'GTQ', + 'GGP', + 'GYD', + 'HNL', + 'HKD', + 'HUF', + 'ISK', + 'INR', + 'IDR', + 'IRR', + 'IMP', + 'ILS', + 'JMD', + 'JPY', + 'JEP', + 'KZT', + 'KPW', + 'KRW', + 'KGS', + 'LAK', + 'LVL', + 'LBP', + 'LRD', + 'LTL', + 'MKD', + 'MYR', + 'MUR', + 'MXN', + 'MNT', + 'MZN', + 'NAD', + 'NPR', + 'ANG', + 'NZD', + 'NIO', + 'NGN', + 'NOK', + 'OMR', + 'PKR', + 'PAB', + 'PYG', + 'PEN', + 'PHP', + 'PLN', + 'QAR', + 'RON', + 'RUB', + 'SHP', + 'SAR', + 'RSD', + 'SCR', + 'SGD', + 'SBD', + 'SOS', + 'ZAR', + 'LKR', + 'SEK', + 'CHF', + 'SRD', + 'SYP', + 'TWD', + 'THB', + 'TTD', + 'TRY', + 'TRL', + 'TVD', + 'UAH', + 'GBP', + 'USD', + 'UYU', + 'UZS', + 'VEF', + 'VND', + 'YER', + 'ZWD', + name='currency', + ), + nullable=False, + comment='The currency of this price as for ISO 4217.', + ), + sa.Column( + 'price', + sa.Numeric(precision=19, scale=4), + nullable=False, + comment='The value.', + ), + sa.Column( + 'software', + sa.Enum('Ereuse', name='pricesoftware'), + nullable=True, + comment='The software used to compute this price,\n if the price was computed automatically. This field is None\n if the price has been manually set.\n ', + ), + sa.Column( + 'version', + teal.db.StrictVersionType(), + nullable=True, + comment='The version of the software, or None.', + ), + sa.Column( + 'rating_id', + postgresql.UUID(as_uuid=True), + nullable=True, + comment='The Rate used to auto-compute\n this price, if it has not been set manually.\n ', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action_with_one_device.id'], + ), + sa.ForeignKeyConstraint( + ['rating_id'], + [f'{get_inv()}.rate.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # ProofDataWipe table - op.create_table('proof_data_wipe', - sa.Column('date', sa.DateTime(), nullable=False), - sa.Column('result', sa.Boolean(), nullable=False, comment='Identifies proof datawipe as a result.'), - sa.Column('proof_author_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('erasure_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['erasure_id'], [f'{get_inv()}.erase_basic.id'], ), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), - sa.ForeignKeyConstraint(['proof_author_id'], ['common.user.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'proof_data_wipe', + sa.Column('date', sa.DateTime(), nullable=False), + sa.Column( + 'result', + sa.Boolean(), + nullable=False, + comment='Identifies proof datawipe as a result.', + ), + sa.Column('proof_author_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('erasure_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['erasure_id'], + [f'{get_inv()}.erase_basic.id'], + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.proof.id'], + ), + sa.ForeignKeyConstraint( + ['proof_author_id'], + ['common.user.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # PRoofFuntion - op.create_table('proof_function', - sa.Column('disk_usage', sa.Integer(), nullable=True), - sa.Column('proof_author_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('rate_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), - sa.ForeignKeyConstraint(['proof_author_id'], ['common.user.id'], ), - sa.ForeignKeyConstraint(['rate_id'], [f'{get_inv()}.rate.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'proof_function', + sa.Column('disk_usage', sa.Integer(), nullable=True), + sa.Column('proof_author_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('rate_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.proof.id'], + ), + sa.ForeignKeyConstraint( + ['proof_author_id'], + ['common.user.id'], + ), + sa.ForeignKeyConstraint( + ['rate_id'], + [f'{get_inv()}.rate.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # RateComputer table - op.create_table('rate_computer', - sa.Column('processor', sa.Float(decimal_return_scale=2), nullable=True, - comment='The rate of the Processor.'), - sa.Column('ram', sa.Float(decimal_return_scale=2), nullable=True, comment='The rate of the RAM.'), - sa.Column('data_storage', sa.Float(decimal_return_scale=2), nullable=True, - comment="'Data storage rate, like HHD, SSD.'"), - sa.Column('graphic_card', sa.Float(decimal_return_scale=2), nullable=True, - comment='Graphic card rate.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.rate.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'rate_computer', + sa.Column( + 'processor', + sa.Float(decimal_return_scale=2), + nullable=True, + comment='The rate of the Processor.', + ), + sa.Column( + 'ram', + sa.Float(decimal_return_scale=2), + nullable=True, + comment='The rate of the RAM.', + ), + sa.Column( + 'data_storage', + sa.Float(decimal_return_scale=2), + nullable=True, + comment="'Data storage rate, like HHD, SSD.'", + ), + sa.Column( + 'graphic_card', + sa.Float(decimal_return_scale=2), + nullable=True, + comment='Graphic card rate.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.rate.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # SnapshotRequest table - op.create_table('snapshot_request', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('request', sa.JSON(), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.snapshot.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'snapshot_request', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('request', sa.JSON(), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.snapshot.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Step table - op.create_table('step', - sa.Column('erasure_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('type', sa.Unicode(length=32), nullable=False), - sa.Column('num', sa.SmallInteger(), nullable=False), - sa.Column('severity', teal.db.IntEnum(Severity), nullable=False), - sa.Column('start_time', sa.TIMESTAMP(timezone=True), nullable=False, - comment='When the action starts. For some actions like\n reservations the time when they are available, for others like renting\n when the renting starts.\n '), - sa.Column('end_time', sa.TIMESTAMP(timezone=True), nullable=False, - comment='When the action ends. For some actions like reservations\n the time when they expire, for others like renting\n the time the end rents. For punctual actions it is the time \n they are performed; it differs with ``created`` in which\n created is the where the system received the action.\n '), - sa.ForeignKeyConstraint(['erasure_id'], [f'{get_inv()}.erase_basic.id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('erasure_id', 'num'), - schema=f'{get_inv()}' - ) + op.create_table( + 'step', + sa.Column('erasure_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('type', sa.Unicode(length=32), nullable=False), + sa.Column('num', sa.SmallInteger(), nullable=False), + sa.Column('severity', teal.db.IntEnum(Severity), nullable=False), + sa.Column( + 'start_time', + sa.TIMESTAMP(timezone=True), + nullable=False, + comment='When the action starts. For some actions like\n reservations the time when they are available, for others like renting\n when the renting starts.\n ', + ), + sa.Column( + 'end_time', + sa.TIMESTAMP(timezone=True), + nullable=False, + comment='When the action ends. For some actions like reservations\n the time when they expire, for others like renting\n the time the end rents. For punctual actions it is the time \n they are performed; it differs with ``created`` in which\n created is the where the system received the action.\n ', + ), + sa.ForeignKeyConstraint( + ['erasure_id'], [f'{get_inv()}.erase_basic.id'], ondelete='CASCADE' + ), + sa.PrimaryKeyConstraint('erasure_id', 'num'), + schema=f'{get_inv()}', + ) - op.create_table('stress_test', - sa.Column('elapsed', sa.Interval(), nullable=False), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'stress_test', + sa.Column('elapsed', sa.Interval(), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) - op.create_table('test_audio', - sa.Column('speaker', sa.Boolean(), nullable=True, comment='Whether the speaker works as expected.'), - sa.Column('microphone', sa.Boolean(), nullable=True, - comment='Whether the microphone works as expected.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test_audio', + sa.Column( + 'speaker', + sa.Boolean(), + nullable=True, + comment='Whether the speaker works as expected.', + ), + sa.Column( + 'microphone', + sa.Boolean(), + nullable=True, + comment='Whether the microphone works as expected.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) - op.create_table('test_bios', - sa.Column('beeps_power_on', sa.Boolean(), nullable=True, - comment='Whether there are no beeps or error\n codes when booting up.\n \n Reference: R2 provision 6 page 23.\n '), - sa.Column('access_range', sa.Enum('A', 'B', 'C', 'D', 'E', name='biosaccessrange'), nullable=True, - comment='Difficulty to modify the boot menu.\n \n This is used as an usability measure for accessing and modifying\n a bios, specially as something as important as modifying the boot\n menu.\n '), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test_bios', + sa.Column( + 'beeps_power_on', + sa.Boolean(), + nullable=True, + comment='Whether there are no beeps or error\n codes when booting up.\n \n Reference: R2 provision 6 page 23.\n ', + ), + sa.Column( + 'access_range', + sa.Enum('A', 'B', 'C', 'D', 'E', name='biosaccessrange'), + nullable=True, + comment='Difficulty to modify the boot menu.\n \n This is used as an usability measure for accessing and modifying\n a bios, specially as something as important as modifying the boot\n menu.\n ', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) - op.create_table('test_camera', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test_camera', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) - op.create_table('test_connectivity', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test_connectivity', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) - op.create_table('test_data_storage', - sa.Column('length', sa.Enum('Short', 'Extended', name='testdatastoragelength'), nullable=False), - sa.Column('status', sa.Unicode(), nullable=False), - sa.Column('lifetime', sa.Interval(), nullable=True), - sa.Column('assessment', sa.Boolean(), nullable=True), - sa.Column('reallocated_sector_count', sa.SmallInteger(), nullable=True), - sa.Column('power_cycle_count', sa.SmallInteger(), nullable=True), - sa.Column('reported_uncorrectable_errors', sa.Integer(), nullable=True), - sa.Column('command_timeout', sa.Integer(), nullable=True), - sa.Column('current_pending_sector_count', sa.SmallInteger(), nullable=True), - sa.Column('offline_uncorrectable', sa.SmallInteger(), nullable=True), - sa.Column('remaining_lifetime_percentage', sa.SmallInteger(), nullable=True), - sa.Column('elapsed', sa.Interval(), nullable=False), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test_data_storage', + sa.Column( + 'length', + sa.Enum('Short', 'Extended', name='testdatastoragelength'), + nullable=False, + ), + sa.Column('status', sa.Unicode(), nullable=False), + sa.Column('lifetime', sa.Interval(), nullable=True), + sa.Column('assessment', sa.Boolean(), nullable=True), + sa.Column('reallocated_sector_count', sa.SmallInteger(), nullable=True), + sa.Column('power_cycle_count', sa.SmallInteger(), nullable=True), + sa.Column('reported_uncorrectable_errors', sa.Integer(), nullable=True), + sa.Column('command_timeout', sa.Integer(), nullable=True), + sa.Column('current_pending_sector_count', sa.SmallInteger(), nullable=True), + sa.Column('offline_uncorrectable', sa.SmallInteger(), nullable=True), + sa.Column('remaining_lifetime_percentage', sa.SmallInteger(), nullable=True), + sa.Column('elapsed', sa.Interval(), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # TestDisplayHinge table - op.create_table('test_display_hinge', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test_display_hinge', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # TestKeyboard table - op.create_table('test_keyboard', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test_keyboard', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # TestPowerAdapter table - op.create_table('test_power_adapter', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test_power_adapter', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # TestTrackpad table - op.create_table('test_trackpad', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'test_trackpad', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # VisualTest table - op.create_table('visual_test', - sa.Column('appearance_range', sa.Enum('Z', 'A', 'B', 'C', 'D', 'E', name='appearancerange'), - nullable=True, - comment='Grades the imperfections that aesthetically affect the device, but not its usage.'), - sa.Column('functionality_range', sa.Enum('A', 'B', 'C', 'D', name='functionalityrange'), - nullable=True, comment='Grades the defects of a device that affect its usage.'), - sa.Column('labelling', sa.Boolean(), nullable=True, - comment='Whether there are tags to be removed.'), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.test.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'visual_test', + sa.Column( + 'appearance_range', + sa.Enum('Z', 'A', 'B', 'C', 'D', 'E', name='appearancerange'), + nullable=True, + comment='Grades the imperfections that aesthetically affect the device, but not its usage.', + ), + sa.Column( + 'functionality_range', + sa.Enum('A', 'B', 'C', 'D', name='functionalityrange'), + nullable=True, + comment='Grades the defects of a device that affect its usage.', + ), + sa.Column( + 'labelling', + sa.Boolean(), + nullable=True, + comment='Whether there are tags to be removed.', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.test.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # Trade table - op.create_table('trade', - sa.Column('shipping_date', sa.TIMESTAMP(timezone=True), nullable=True, - comment='When are the devices going to be ready \n for shipping?\n '), - sa.Column('invoice_number', citext.CIText(), nullable=True, - comment='The id of the invoice so they can be linked.'), - sa.Column('price_id', postgresql.UUID(as_uuid=True), nullable=True, - comment='The price set for this trade. \n If no price is set it is supposed that the trade was\n not payed, usual in donations.\n '), - sa.Column('to_id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('confirms_id', postgresql.UUID(as_uuid=True), nullable=True, - comment='An organize action that this association confirms. \n \n For example, a ``Sell`` or ``Rent``\n can confirm a ``Reserve`` action.\n '), - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.ForeignKeyConstraint(['confirms_id'], [f'{get_inv()}.organize.id'], ), - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.ForeignKeyConstraint(['price_id'], [f'{get_inv()}.price.id'], ), - sa.ForeignKeyConstraint(['to_id'], [f'{get_inv()}.agent.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) + op.create_table( + 'trade', + sa.Column( + 'shipping_date', + sa.TIMESTAMP(timezone=True), + nullable=True, + comment='When are the devices going to be ready \n for shipping?\n ', + ), + sa.Column( + 'invoice_number', + citext.CIText(), + nullable=True, + comment='The id of the invoice so they can be linked.', + ), + sa.Column( + 'price_id', + postgresql.UUID(as_uuid=True), + nullable=True, + comment='The price set for this trade. \n If no price is set it is supposed that the trade was\n not payed, usual in donations.\n ', + ), + sa.Column('to_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column( + 'confirms_id', + postgresql.UUID(as_uuid=True), + nullable=True, + comment='An organize action that this association confirms. \n \n For example, a ``Sell`` or ``Rent``\n can confirm a ``Reserve`` action.\n ', + ), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint( + ['confirms_id'], + [f'{get_inv()}.organize.id'], + ), + sa.ForeignKeyConstraint( + ['id'], + [f'{get_inv()}.action.id'], + ), + sa.ForeignKeyConstraint( + ['price_id'], + [f'{get_inv()}.price.id'], + ), + sa.ForeignKeyConstraint( + ['to_id'], + [f'{get_inv()}.agent.id'], + ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}', + ) # ### end Alembic commands ### @@ -1643,7 +6867,9 @@ def downgrade(): op.drop_table('test', schema=f'{get_inv()}') - op.drop_constraint("snapshot_actions", "action", type_="foreignkey", schema=f'{get_inv()}') + op.drop_constraint( + "snapshot_actions", "action", type_="foreignkey", schema=f'{get_inv()}' + ) op.drop_table('snapshot', schema=f'{get_inv()}') op.drop_table('rate', schema=f'{get_inv()}') @@ -1672,8 +6898,12 @@ def downgrade(): op.drop_table('migrate', schema=f'{get_inv()}') - op.drop_index(op.f('ix_membership_updated'), table_name='membership', schema=f'{get_inv()}') - op.drop_index(op.f('ix_membership_created'), table_name='membership', schema=f'{get_inv()}') + op.drop_index( + op.f('ix_membership_updated'), table_name='membership', schema=f'{get_inv()}' + ) + op.drop_index( + op.f('ix_membership_created'), table_name='membership', schema=f'{get_inv()}' + ) op.drop_table('membership', schema=f'{get_inv()}') op.drop_table('graphic_card', schema=f'{get_inv()}') @@ -1688,7 +6918,11 @@ def downgrade(): op.drop_table('allocate', schema=f'{get_inv()}') - op.drop_index('action_one_device_id_index', table_name='action_with_one_device', schema=f'{get_inv()}') + op.drop_index( + 'action_one_device_id_index', + table_name='action_with_one_device', + schema=f'{get_inv()}', + ) op.drop_table('action_with_one_device', schema=f'{get_inv()}') op.drop_table('action_device', schema=f'{get_inv()}') @@ -1720,8 +6954,16 @@ def downgrade(): op.drop_table('individual', schema=f'{get_inv()}') - op.drop_index(op.f('ix_deliverynote_updated'), table_name='deliverynote', schema=f'{get_inv()}') - op.drop_index(op.f('ix_deliverynote_created'), table_name='deliverynote', schema=f'{get_inv()}') + op.drop_index( + op.f('ix_deliverynote_updated'), + table_name='deliverynote', + schema=f'{get_inv()}', + ) + op.drop_index( + op.f('ix_deliverynote_created'), + table_name='deliverynote', + schema=f'{get_inv()}', + ) op.drop_table('deliverynote', schema=f'{get_inv()}') op.drop_index('parent_index', table_name='component', schema=f'{get_inv()}') @@ -1780,8 +7022,12 @@ def downgrade(): op.drop_table('manufacturer', schema='common') - op.drop_index(op.f('ix_common_inventory_updated'), table_name='inventory', schema='common') - op.drop_index(op.f('ix_common_inventory_created'), table_name='inventory', schema='common') + op.drop_index( + op.f('ix_common_inventory_updated'), table_name='inventory', schema='common' + ) + op.drop_index( + op.f('ix_common_inventory_created'), table_name='inventory', schema='common' + ) op.drop_index('id_hash', table_name='inventory', schema='common') op.drop_table('inventory', schema='common') diff --git a/ereuse_devicehub/parser/computer.py b/ereuse_devicehub/parser/computer.py index b41ce833..50a8bf61 100644 --- a/ereuse_devicehub/parser/computer.py +++ b/ereuse_devicehub/parser/computer.py @@ -7,8 +7,8 @@ from math import hypot from typing import Iterator, List, Optional, TypeVar import dateutil.parser -from ereuse_utils import getter, text -from ereuse_utils.nested_lookup import ( +from ereuse_devicehub.ereuse_utils import getter, text +from ereuse_devicehub.ereuse_utils.nested_lookup import ( get_nested_dicts_with_key_containing_value, get_nested_dicts_with_key_value, ) diff --git a/ereuse_devicehub/parser/utils.py b/ereuse_devicehub/parser/utils.py index e36990fe..3d912111 100644 --- a/ereuse_devicehub/parser/utils.py +++ b/ereuse_devicehub/parser/utils.py @@ -5,7 +5,7 @@ import struct from contextlib import contextmanager from enum import Enum -from ereuse_utils import Dumpeable +from ereuse_devicehub.ereuse_utils import Dumpeable class Severity(Enum): diff --git a/ereuse_devicehub/resources/action/views/views.py b/ereuse_devicehub/resources/action/views/views.py index a9c1d664..71ec908e 100644 --- a/ereuse_devicehub/resources/action/views/views.py +++ b/ereuse_devicehub/resources/action/views/views.py @@ -4,7 +4,7 @@ from datetime import timedelta from distutils.version import StrictVersion from uuid import UUID -import ereuse_utils +import ereuse_devicehub.ereuse_utils import jwt from flask import current_app as app from flask import g, request @@ -203,7 +203,7 @@ def decode_snapshot(data): data['data'], app.config['JWT_PASS'], algorithms="HS256", - json_encoder=ereuse_utils.JSONEncoder, + json_encoder=ereuse_devicehub.ereuse_utils.JSONEncoder, ) except jwt.exceptions.InvalidSignatureError as err: txt = 'Invalid snapshot' diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 87ef6fd6..0867e921 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -12,7 +12,7 @@ from typing import Dict, List, Set from boltons import urlutils from citext import CIText -from ereuse_utils.naming import HID_CONVERSION_DOC +from ereuse_devicehub.ereuse_utils.naming import HID_CONVERSION_DOC from flask import current_app as app from flask import g, request from more_itertools import unique_everseen diff --git a/ereuse_devicehub/resources/inventory/schema.py b/ereuse_devicehub/resources/inventory/schema.py index 57b157d5..d1579ef0 100644 --- a/ereuse_devicehub/resources/inventory/schema.py +++ b/ereuse_devicehub/resources/inventory/schema.py @@ -1,4 +1,4 @@ -import teal.marshmallow +import ereuse_devicehub.teal.marshmallow from marshmallow import fields as mf from ereuse_devicehub.resources.schemas import Thing @@ -7,4 +7,6 @@ from ereuse_devicehub.resources.schemas import Thing class Inventory(Thing): id = mf.String(dump_only=True) name = mf.String(dump_only=True) - tag_provider = teal.marshmallow.URL(dump_only=True, data_key='tagProvider') + tag_provider = ereuse_devicehub.teal.marshmallow.URL( + dump_only=True, data_key='tagProvider' + ) diff --git a/ereuse_devicehub/resources/tag/__init__.py b/ereuse_devicehub/resources/tag/__init__.py index 72b11706..85ec4084 100644 --- a/ereuse_devicehub/resources/tag/__init__.py +++ b/ereuse_devicehub/resources/tag/__init__.py @@ -2,7 +2,7 @@ import csv import pathlib from click import argument, option -from ereuse_utils import cli +from ereuse_devicehub.ereuse_utils import cli from ereuse_devicehub.db import db from ereuse_devicehub.resources.device.definitions import DeviceDef diff --git a/ereuse_devicehub/teal/client.py b/ereuse_devicehub/teal/client.py index f18a1d53..d2ab1210 100644 --- a/ereuse_devicehub/teal/client.py +++ b/ereuse_devicehub/teal/client.py @@ -1,9 +1,9 @@ from typing import Any, Iterable, Tuple, Type, Union from boltons.urlutils import URL -from ereuse_utils.test import JSON -from ereuse_utils.test import Client as EreuseUtilsClient -from ereuse_utils.test import Res +from ereuse_devicehub.ereuse_utils.test import JSON +from ereuse_devicehub.ereuse_utils.test import Client as EreuseUtilsClient +from ereuse_devicehub.ereuse_utils.test import Res from werkzeug.exceptions import HTTPException from ereuse_devicehub.teal.marshmallow import ValidationError diff --git a/ereuse_devicehub/teal/db.py b/ereuse_devicehub/teal/db.py index 08c02252..7f0b8c7f 100644 --- a/ereuse_devicehub/teal/db.py +++ b/ereuse_devicehub/teal/db.py @@ -7,7 +7,7 @@ from typing import Any, Type, Union from boltons.typeutils import classproperty from boltons.urlutils import URL as BoltonsUrl -from ereuse_utils import if_none_return_none +from ereuse_devicehub.ereuse_utils import if_none_return_none from flask_sqlalchemy import BaseQuery from flask_sqlalchemy import Model as _Model from flask_sqlalchemy import SignallingSession diff --git a/ereuse_devicehub/teal/json_util.py b/ereuse_devicehub/teal/json_util.py index 0219fb99..fd1df357 100644 --- a/ereuse_devicehub/teal/json_util.py +++ b/ereuse_devicehub/teal/json_util.py @@ -1,10 +1,10 @@ -import ereuse_utils +import ereuse_devicehub.ereuse_utils from flask.json import JSONEncoder as FlaskJSONEncoder from sqlalchemy.ext.baked import Result from sqlalchemy.orm import Query -class TealJSONEncoder(ereuse_utils.JSONEncoder, FlaskJSONEncoder): +class TealJSONEncoder(ereuse_devicehub.ereuse_utils.JSONEncoder, FlaskJSONEncoder): def default(self, obj): if isinstance(obj, (Result, Query)): return tuple(obj) diff --git a/ereuse_devicehub/teal/marshmallow.py b/ereuse_devicehub/teal/marshmallow.py index af5f0eca..72189930 100644 --- a/ereuse_devicehub/teal/marshmallow.py +++ b/ereuse_devicehub/teal/marshmallow.py @@ -4,7 +4,7 @@ from typing import Type, Union import colour from boltons import strutils, urlutils -from ereuse_utils import if_none_return_none +from ereuse_devicehub.ereuse_utils import if_none_return_none from flask import current_app as app from flask import g from marshmallow import utils diff --git a/ereuse_devicehub/teal/query.py b/ereuse_devicehub/teal/query.py index f1e3f370..e6103719 100644 --- a/ereuse_devicehub/teal/query.py +++ b/ereuse_devicehub/teal/query.py @@ -1,7 +1,7 @@ import json from json import JSONDecodeError -from ereuse_utils import flatten_mixed +from ereuse_devicehub.ereuse_utils import flatten_mixed from marshmallow import Schema as MarshmallowSchema from marshmallow.fields import Boolean, Field, List, Nested, Str, missing_ from sqlalchemy import Column, between, or_ diff --git a/ereuse_devicehub/teal/resource.py b/ereuse_devicehub/teal/resource.py index 31d33599..6ba998cb 100644 --- a/ereuse_devicehub/teal/resource.py +++ b/ereuse_devicehub/teal/resource.py @@ -4,7 +4,7 @@ from typing import Callable, Iterable, Iterator, Tuple, Type, Union import inflection from anytree import PreOrderIter from boltons.typeutils import classproperty, issubclass -from ereuse_utils.naming import Naming +from ereuse_devicehub.ereuse_utils.naming import Naming from flask import Blueprint, current_app, g, request, url_for from flask.json import jsonify from flask.views import MethodView diff --git a/ereuse_devicehub/teal/teal.py b/ereuse_devicehub/teal/teal.py index 7b341b49..6acf33c1 100644 --- a/ereuse_devicehub/teal/teal.py +++ b/ereuse_devicehub/teal/teal.py @@ -2,12 +2,12 @@ import inspect from typing import Dict, Type import click_spinner -import ereuse_utils +import ereuse_devicehub.ereuse_utils import flask_cors from anytree import Node from apispec import APISpec from click import option -from ereuse_utils import ensure_utf8 +from ereuse_devicehub.ereuse_utils import ensure_utf8 from flask import Flask, jsonify from flask.globals import _app_ctx_stack from flask_sqlalchemy import SQLAlchemy @@ -284,7 +284,7 @@ class Teal(Flask): return jsonify(self._apidocs) -class DumpeableHTTPException(ereuse_utils.Dumpeable): +class DumpeableHTTPException(ereuse_devicehub.ereuse_utils.Dumpeable): """Exceptions that inherit this class will be able to dump to dicts and JSONs. """ diff --git a/tests/conftest.py b/tests/conftest.py index 8e5c52d1..ac75ecc8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,7 +6,6 @@ from datetime import datetime from pathlib import Path import boltons.urlutils -import ereuse_utils import jwt import pytest import yaml @@ -14,6 +13,7 @@ from decouple import config from psycopg2 import IntegrityError from sqlalchemy.exc import ProgrammingError +from ereuse_devicehub import ereuse_utils from ereuse_devicehub.api.views import api from ereuse_devicehub.client import Client, UserClient, UserClientFlask from ereuse_devicehub.config import DevicehubConfig