import bpy
import os
from io_scs_tools.utils.printout import lprint
from io_scs_tools.utils import get_scs_globals as _get_scs_globals

def strip_sep(path):
    """Strips double path separators (slashes and backslashes) on the start and the end of the given path
    :param path: path to strip separators from
    :type path: str
    :return: new stripped path
    :rtype: str
    """
    return path.strip("\\\\").strip("//")

def get_filename(path, with_ext=True):
    """Returns file name from given file, with or without extension.
    It finds last "os.sep" inside string and returns tail.
    :param path: path with file name
    :type path: str
    :param with_ext: return file name with extension or not
    :type with_ext: bool
    :return: file name with or without extension
    :rtype: str
    """

    # find last separator; prefer os.sep otherwise search for normal slash
    last_sep = path.rfind(os.sep)
    if last_sep < 0:
        last_sep = path.rfind("/")

    new_path = path[last_sep + 1:]
    if not with_ext and new_path.rfind(".") > 0:
        new_path = new_path[:new_path.rfind(".")]
    return new_path

def repair_path(filepath):
    """Takes a Blender filepath and tries to make it a valid absolute path."""
    if filepath != '':
        # print('0 filepath:\n"%s"' % filepath)
        filepath = bpy.path.abspath(filepath, start=None, library=None)  # make the path absolute
        # print('1 filepath:\n"%s"' % filepath)
        filepath = os.path.normpath(filepath)  # repair things like "...\dir\dir\..\dir\..."
        # print('2 filepath:\n"%s"' % filepath)
    return filepath

def relative_path(base_path, path):
    """Takes a base path and other path and returns the relative version of the second path
    if possible, otherwise it returns the original one (absolute path)."""
    repaired_base_path = repair_path(base_path)
    # print('repaired_base_path:\n\t"%s"' % repaired_base_path)
    repaired_path = repair_path(path)
    # print('repaired_path:\n\t"%s"' % repaired_path)
    if len(repaired_base_path) > 2:
        # presuming that equality of first two chars means we are on same mount point
        if startswith(repaired_path[:2], repaired_base_path[:2]):
            rel_path = os.path.relpath(repaired_path, repaired_base_path).replace("\\", "/")
            # print('rel_path:\n\t"%s"' % rel_path)
            if not rel_path.startswith("//"):
                rel_path = "//" + rel_path
            return rel_path
        else:
            lprint("W Not possible to create a relative path! Returning absolute path (%s)", (repaired_path,))
            return repaired_path
    else:
        lprint("W No base path specified! It's not possible to create a relative path! Returning absolute path (%s)", (repaired_path,))
        return repaired_path

def get_abs_path(path_in, subdir_path='', is_dir=False, skip_mod_check=False):
    """Gets absolute path to the "SCS Project Base Path" if given path is relative (starts with: "//"),
    otherwise original path is returned.
    If relative path is existing and valid, it returns the absolute path, otherwise None.
    Optionally a subdir_path can be provided, which will be added to the 'SCS Project Base Path'.
    If skipping of mod check is not specified then function will also try to look in two
    parent base directories, in the case "SCS Project Base Path" is currently set to mod/dlc package.

    :param path_in: Absolute or relative path to current 'SCS Project Base path'
    :type path_in: str
    :param subdir_path: Additional subdirs can be provided, they will be added to the 'SCS Project Base Path'
    :type subdir_path: str
    :param is_dir: flag specifying if given path should be directory
    :type is_dir: bool
    :param skip_mod_check: flag specifying if check for dlc/mod should be skipped
    :type skip_mod_check: bool
    :return: Absolute path or None
    :rtype: str
    """
    root_path = _get_scs_globals().scs_project_path
    if subdir_path != '':
        root_path = os.path.join(root_path, subdir_path)

    if path_in.startswith("//"):
        if len(root_path) > 2:
            result = os.path.join(root_path, path_in[2:])
        else:
            result = None
    else:
        result = path_in

    existance_check = os.path.isdir if is_dir else os.path.isfile

    if result is not None and not existance_check(result) and not skip_mod_check:
        result = get_abs_path(path_in, subdir_path="../base", is_dir=is_dir, skip_mod_check=True)

    if result is not None and not existance_check(result) and not skip_mod_check:
        result = get_abs_path(path_in, subdir_path="../../base", is_dir=is_dir, skip_mod_check=True)

    # finally if file/dir not found return correct abs path, not the one from parent dirs
    if result is not None and not existance_check(result) and not skip_mod_check:
        result = get_abs_path(path_in, subdir_path=subdir_path, is_dir=is_dir, skip_mod_check=True)

    return result

def is_valid_shader_texture_path(shader_texture):
    """It returns True if there is valid Shader Texture file, otherwise False.

    :param shader_texture: SCS texture path, can be absolute or relative
    :type shader_texture: str
    :return: True if there is valid Shader Texture file, otherwise False
    :rtype: bool
    """
    if shader_texture != "":

        if shader_texture.startswith("//"):  # RELATIVE PATH

            shader_texture_abs_path = get_abs_path(shader_texture)

            if shader_texture_abs_path and os.path.isfile(shader_texture_abs_path):
                return True

        else:  # ABSOLUTE PATH

            if os.path.isfile(shader_texture):
                return True

    return False

def is_valid_shader_presets_library_path():
    """It returns True if there is valid "*.txt" file in
    the Shader Presets Library directory, otherwise False."""
    shader_presets_filepath = _get_scs_globals().shader_presets_filepath
    if shader_presets_filepath != "":
        if shader_presets_filepath.startswith("//"):  # RELATIVE PATH
            shader_presets_abs_path = get_abs_path(shader_presets_filepath)
            if shader_presets_abs_path:
                if os.path.isfile(shader_presets_abs_path):
                    return True
        else:  # ABSOLUTE PATH
            if os.path.isfile(shader_presets_filepath):
                return True
    return False

def is_valid_trigger_actions_rel_path():
    """It returns True if there is valid "*.sii" file in
    the Trigger Actions directory, otherwise False."""
    trig_actions_abs_path = get_abs_path(_get_scs_globals().trigger_actions_rel_path)
    if trig_actions_abs_path:
        if os.path.isfile(trig_actions_abs_path):
            return True
        else:
            return False
    else:
        return False

def is_valid_sign_library_rel_path():
    """It returns True if there is valid "*.sii" file in
    the Sign Library directory, otherwise False."""
    sign_library_abs_path = get_abs_path(_get_scs_globals().sign_library_rel_path)
    if sign_library_abs_path:
        if os.path.isfile(sign_library_abs_path):
            return True
        else:
            return False
    else:
        return False

def is_valid_tsem_library_rel_path():
    """It returns True if there is valid "*.sii" file in
    the Traffic Semaphore Profile Library directory, otherwise False."""
    tsem_library_abs_path = get_abs_path(_get_scs_globals().tsem_library_rel_path)
    if tsem_library_abs_path:
        if os.path.isfile(tsem_library_abs_path):
            return True
        else:
            return False
    else:
        return False

def is_valid_traffic_rules_library_rel_path():
    """It returns True if there is valid "*.sii" file in
    the Traffic Rules Library directory, otherwise False."""
    traffic_rules_library_abs_path = get_abs_path(_get_scs_globals().traffic_rules_library_rel_path)
    if traffic_rules_library_abs_path:
        if os.path.isfile(traffic_rules_library_abs_path):
            return True
        else:
            return False
    else:
        return False

def is_valid_hookup_library_rel_path():
    """It returns True if there is at least one "*.sii" file in
    the resulting CgFX Library directory, otherwise False."""
    hookup_library_abs_path = get_abs_path(_get_scs_globals().hookup_library_rel_path, is_dir=True)
    if hookup_library_abs_path:
        for root, dirs, files in os.walk(hookup_library_abs_path):
            for file in files:
                if file.endswith(".sii"):
                    return True
            return False
    else:
        return False

def is_valid_matsubs_library_rel_path():
    """It returns True if there is valid "*.db" file in
    the Material Substance Library directory, otherwise False."""
    matsubs_library_abs_path = get_abs_path(_get_scs_globals().matsubs_library_rel_path)
    # print(' matsubs_library_abs_path: %r' % str(matsubs_library_abs_path))
    if matsubs_library_abs_path:
        if os.path.isfile(matsubs_library_abs_path):
            return True
        else:
            return False
    else:
        return False

def get_addon_installation_paths():
    """Returns a list of paths to the directories where the addon can be installed."""
    script_paths = bpy.utils.script_paths()
    addon_dirs = ('addons', 'addons_contrib')
    script_locations = []

    for script_path in script_paths:
        for addon_dir in addon_dirs:
            script_locations.append(os.path.join(script_path, addon_dir, 'io_scs_tools'))

    scs_installation_dirs = []
    for location in script_locations:
        if os.path.isdir(location):
            scs_installation_dirs.append(location)

    if len(scs_installation_dirs) == 0:
        lprint('''\n\nE The installation directory of "SCS Blender Tools" couldn't be detected! (Shouldn't happen!)\n''')
    elif len(scs_installation_dirs) > 1:
        lprint('\n\nW More than one installation of "SCS Blender Tools" detected!\n\t  Please remove redundant installations so the only one '
               'remain.\n')

    return scs_installation_dirs

def get_shader_presets_filepath():
    """Returns a valid filepath to "shader_presets.txt" file. If the file doesn't exists,
    the empty string is returned and Shader Presets won't be available."""
    scs_installation_dirs = get_addon_installation_paths()

    shader_presets_file = ''
    for location in scs_installation_dirs:
        test_path = os.path.join(location, 'shader_presets.txt')
        if os.path.isfile(test_path):
            shader_presets_file = test_path
            break

    return shader_presets_file

def get_texture_path_from_tobj(tobj_filepath, raw_value=False):
    """Get absolute path of texture from given tobj filepath.
    If raw value is requested returned path is direct value written in TOBJ.
    NOTE: there is no safety check if file exists.

    :param tobj_filepath: absolute tobj file path
    :type tobj_filepath: str
    :param raw_value: flag for indicating if texture path shall be returned as it's written in TOBJ
    :type raw_value: bool
    :return: absolute texture file path if found or None
    :rtype: str | None
    """

    from io_scs_tools.internals.containers.tobj import TobjContainer

    container = TobjContainer.read_data_from_file(tobj_filepath, skip_validation=raw_value)
    tobj_dir, tobj_filename = os.path.split(tobj_filepath)

    if container is None:
        return None

    if raw_value:
        return container.map_names[0]

    if container.map_names[0][0] == "/":
        return get_abs_path("//" + container.map_names[0][1:])

    return os.path.join(tobj_dir, container.map_names[0])

def get_texture_extens_and_strip_path(texture_path):
    """Gets all supported texture extensions and strips given input path for any of it.

    :param texture_path: shader texture raw path value
    :type texture_path: str
    :return: list of extensions and stripped path as tuple
    :rtype: tuple[list[str], str]
    """

    extensions = [".tobj", ".tga", ".png"]

    # strip of any extensions ( endswith is most secure, because of possible multiple extensions )
    if texture_path.endswith(".tobj"):

        extensions.insert(0, texture_path[-5:])
        texture_path = texture_path[:-5]

    elif texture_path.endswith(".tga") or texture_path.endswith(".png"):

        extensions.insert(0, texture_path[-4:])
        texture_path = texture_path[:-4]

    return extensions, texture_path

def get_scs_texture_str(texture_string):
    """Get texture string as presented in SCS files: "/material/environment/vehicle_reflection"
    without any file extensions. Input path can also have texture object extension or supported images extensions.
    Path will be searched and returned in this order:
    1. relative path on current SCS Project Base Path
    2. relative path on parent base dirs of current SCS Project Base Path in the case of mod/dlc
    3. find absolute file path
    4. return unchanged texture string path

    :param texture_string: texture string for which texture should be found e.g.: "/material/environment/vehicle_reflection"
    :type texture_string: str
    :return: relative path to texture object or absolute path to texture object or uncanged texture string
    :rtype: str
    """

    scs_project_path = _get_scs_globals().scs_project_path
    orig_texture_string = texture_string

    # remove any directory separators left overs from different platform
    texture_string = texture_string.replace("/", os.sep).replace("\\", os.sep)

    extensions, texture_string = get_texture_extens_and_strip_path(texture_string)

    # if texture string starts with scs project path we can directly strip of project path
    if startswith(texture_string, scs_project_path):
        texture_string = texture_string[len(scs_project_path):]
    else:  # check if texture string came from base project while scs project path is in dlc/mod folder

        # first find longest matching path
        i = 1
        while startswith(scs_project_path, texture_string[:i]) and i < len(texture_string):
            i += 1

        # now check if provided texture string is the same as:
        # current scs project path + one or two directories up + non matched path of the part
        for infix in ("..", ".." + os.sep + ".."):

            nonmatched_path_part = texture_string[i - 2:]

            modif_texture_string = os.path.join(scs_project_path, infix + nonmatched_path_part)
            # if one or two directories up is the same path as texture string
            # and non matched path part is starting with /base we got a hit:
            # resulting relative path is non matched path part with stripped "/base" start
            if is_samepath(modif_texture_string, texture_string) and startswith(nonmatched_path_part, os.sep + "base"):
                texture_string = nonmatched_path_part[5:]
                break

    # check for relative TOBJ, TGA, PNG
    for ext in extensions:
        texture_path = get_abs_path("//" + texture_string.strip(os.sep) + ext)
        if texture_path and os.path.isfile(texture_path):
            return "//" + texture_string.replace(os.sep, "/").strip("/") + ext

    # check for absolute TOBJ, TGA, PNG
    for ext in extensions:
        texture_path = get_abs_path(texture_string + ext, skip_mod_check=True)
        if texture_path and os.path.isfile(texture_path):
            return texture_string.replace(os.sep, "/") + ext

    return orig_texture_string

def get_tobj_path_from_shader_texture(shader_texture, check_existance=True):
    """Gets TOBJ path from shader texture value if exists, otherwise returning None.

    :param shader_texture: shader texture raw path value
    :type shader_texture: str
    :param check_existance: flag indicating if tobj path should be also checked for existance
    :type check_existance: bool
    :return: TOBJ absolute path or None if not found
    :rtype: str | None
    """

    # strip of any extensions ( endswith is most secure, because of possible multiple extensions )
    if shader_texture.endswith(".tobj"):
        tobj_filpath = shader_texture
    elif shader_texture.endswith(".tga") or shader_texture.endswith(".png"):
        tobj_filpath = shader_texture[:-4] + ".tobj"
    else:
        tobj_filpath = shader_texture + ".tobj"

    # NOTE: if there is no existence check then we also shouldn't check for mods file system structure
    tobj_filpath = get_abs_path(tobj_filpath, skip_mod_check=not check_existance)
    if not check_existance or (tobj_filpath and os.path.isfile(tobj_filpath)):
        return tobj_filpath

    return None

def get_skeleton_relative_filepath(armature, directory, default_name):
    """Get's skeleton relative path to given directory. This path can be used for linking
    skeletons in PIM and PIA files.

    :param armature: armature object which will be used as scs skeleton
    :type armature: bpy.types.Object
    :param directory: directory from which relative path of skeleton should be gotten
    :type directory: str
    :param default_name: if custom path is empty this name will be used as the name of pis file
    :type default_name: str
    :return: relative path to predicted PIS file of given armature
    :rtype: str
    """

    skeleton_custom_dir = armature.scs_props.scs_skeleton_custom_export_dirpath
    skeleton_custom_name = armature.scs_props.scs_skeleton_custom_name

    skeleton_path = ""
    if skeleton_custom_dir != "":
        if skeleton_custom_dir.startswith("//"):
            skeleton_path = os.path.relpath(os.path.join(_get_scs_globals().scs_project_path, skeleton_custom_dir[2:]), directory)
        else:
            lprint("E Custom skeleton export path is not relative to SCS Project Base Path.\n\t   " +
                   "Custom path will be ignored, which might lead to wrongly linked skeleton file inside PIM and PIA files.")

    skeleton_name = (skeleton_custom_name if skeleton_custom_name != "" else default_name) + ".pis"

    return os.path.join(skeleton_path, skeleton_name)

def get_animations_relative_filepath(scs_root, directory):
    """Get's skeleton relative path to given directory. This path can be used for linking
    skeletons in PIM and PIA files.

    :param scs_root: scs root object of this animation
    :type scs_root: bpy.types.Object
    :param directory: directory from which relative path of animaton should be gotten
    :type directory: str
    :return: relative path to predicted PIS file of given armature
    :rtype: str
    """

    anims_path = ""

    if scs_root.scs_props.scs_root_object_allow_anim_custom_path:
        animations_custom_dir = scs_root.scs_props.scs_root_object_anim_export_filepath

        if animations_custom_dir != "":

            if animations_custom_dir.startswith("//"):
                anims_path = os.path.relpath(os.path.join(_get_scs_globals().scs_project_path, animations_custom_dir[2:]), directory)
            else:
                return None

    return anims_path

def get_global_export_path():
    """Gets global export path.
    If default export path is empty and blend file is saved inside current scs project path  -> return blend file dir;
    Otherwise return scs project path combined with default export path.
    :return: global export path defined by directory of saved blend file and default export path from settings
    :rtype: str
    """

    scs_project_path = _get_scs_globals().scs_project_path
    is_blend_file_within_base = bpy.data.filepath != "" and startswith(bpy.data.filepath, scs_project_path)
    default_export_path = bpy.context.scene.scs_props.default_export_filepath

    # if not set try to use Blender filepath
    if default_export_path == "" and is_blend_file_within_base:
        return os.path.dirname(bpy.data.filepath)
    else:
        return os.path.join(scs_project_path, default_export_path.strip("//"))

def get_custom_scs_root_export_path(root_object):
    """Gets custom export file path for given SCS Root Object.
    If custom export path is empty and blend file is saved inside current scs project path -> return blend file dir;
    Otherwise return scs porject path combined with custom scs root export path.
    :param root_object: scs root object
    :type root_object: bpy.types.Object
    :return: custom export directory path of given SCS Root Object; None if custom export for SCS Root is disabled
    :rtype: str | None
    """
    scs_project_path = _get_scs_globals().scs_project_path
    is_blend_file_within_base = bpy.data.filepath != "" and startswith(bpy.data.filepath, scs_project_path)

    custom_filepath = None
    if root_object.scs_props.scs_root_object_allow_custom_path:
        scs_root_export_path = root_object.scs_props.scs_root_object_export_filepath
        # if not set try to use Blender filepath
        if scs_root_export_path == "" and is_blend_file_within_base:
            custom_filepath = os.path.dirname(bpy.data.filepath)
        else:
            custom_filepath = os.path.join(scs_project_path, scs_root_export_path.strip("//"))

    return custom_filepath

def get_all_infixed_file_paths(filepath, include_given_path=True):
    """Gets files from same directory using any infixed word without,
    however dot can not appear in infix.

    :param filepath: absolute filepath which shall be checked for any infixed files
    :type filepath: str
    :param include_given_path: if True given file path will be included in returning list otherwise no
    :type include_given_path: bool
    :return: list of all infixed files; optionally given filepath can be added to result list too
    :rtype: list[str]
    """

    infixed_filepaths = [filepath] if include_given_path else []

    orig_dir, orig_file = os.path.split(filepath)

    # if original directory doesn't exists skip searching for any infix files
    if not os.path.isdir(orig_dir):
        return infixed_filepaths

    last_ext_i = orig_file.rfind(".")

    orig_file_prefix = orig_file[:last_ext_i]
    orig_file_postfix = orig_file[last_ext_i:]

    for file in os.listdir(orig_dir):

        # if given file path is already prefixed make sure to ignore it
        if file == orig_file:
            continue

        if file.startswith(orig_file_prefix) and file.endswith(orig_file_postfix) and file.count(".") == 2:
            infixed_filepaths.append(os.path.join(orig_dir, file))

    return infixed_filepaths

def startswith(path1, path2):
    """Checks if first given path starts with second given path.
    It also takes into account windows drive letter which can be big or small.

    :param path1: first path
    :type path1: str
    :param path2: second path
    :type path2: str
    :return: True if path1 starts with path2; False otherwise
    :rtype: bool
    """

    norm_path1 = full_norm(path1)
    norm_path2 = full_norm(path2)

    return norm_path1.startswith(norm_path2)

def is_samepath(path1, path2):
    """Checks if paths are the same
    It also takes into account windows drive letter which can be big or small.

    :param path1: first path
    :type path1: str
    :param path2: second path
    :type path2: str
    :return: True if path1 starts with path2; False otherwise
    :rtype: bool
    """

    norm_path1 = full_norm(path1)
    norm_path2 = full_norm(path2)

    return norm_path1 == norm_path2

def full_norm(path1):
    """Normalize path.
    It also takes into account windows drive letter which can be big or small.

    :param path1: path
    :type path1: str
    :return: normalized path
    :rtype: str
    """

    norm_path1 = os.path.normpath(path1)
    norm_path1 = os.path.normcase(norm_path1)

    return norm_path1

def readable_norm(path):
    """Normalize path in nice human readable form.
    On windows it also converts backslashes to forward ones, to have cross platform output.

    :param path: path to normalize
    :type path: str
    :return: normalized path
    :rtype: str
    """
    norm_path = os.path.normpath(path)
    norm_path = norm_path.replace("\\", "/")

    return norm_path


from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator
from ..models import Posts
from django.db.models import Q, Count

def index(request, category=None):
    page = request.GET.get('page', '1') # 페이지를 읽어온다. 없을경우 1을 뱉는다 ?page =
    kw = request.GET.get('kw', '') # 검색어

    if category == 'freeboard':
        posts_list = Posts.objects.filter(category_id=1).order_by('-create_date')
    elif category == 'diary':
        posts_list = Posts.objects.filter(category_id=2).order_by('-create_date')
    elif category == 'QnA':
        posts_list = Posts.objects.filter(category_id=3).order_by('-create_date')
    else:
        posts_list = Posts.objects.order_by('-create_date')

    if kw:
        posts_list = posts_list.filter( # icontains 대소문자 구분없음, 있음은 contains
            Q(subject__icontains=kw) | # 제목 검색
            Q(content__icontains=kw) | # 내용 검색
            Q(reply__content__icontains=kw) | # 댓글 내용
            Q(reply__author__username__icontains=kw) | # 댓글 작성자
            Q(author__username__icontains=kw) # 일기 작성자
        ).distinct() # 중복제거
    paginator = Paginator(posts_list, 10) # 한페이지에 보여줄 페이지 갯수 10
    page_obj = paginator.get_page(page) # 페이지 오브젝트에 담아서 보여줌
    context = {'category': category, 'posts_list': page_obj, 'page': page, 'kw': kw}

    return render(request, 'main/posts_list.html', context)

def detail(request, posts_id):
    posts = get_object_or_404(Posts, pk=posts_id)

    page = request.GET.get('page', '1')
    kw = request.GET.get('kw', 'first') # 정렬방법

    if kw == 'voter': # 추천순
        paginator = Paginator(posts.reply_set.all().annotate(num_votes=Count('voter')).order_by('-num_votes'), 10)
    elif kw == 'latest': # 최신순
        paginator = Paginator(posts.reply_set.all().order_by('-id'), 10)
    else: # 등록순
        paginator = Paginator(posts.reply_set.all(), 10)

    page_obj = paginator.get_page(page)

    context = {'posts': posts, 'reply_list': page_obj, 'page': page, 'kw': kw}
    return render(request, 'main/posts_detail.html', context)


from typing import Text
import requests
import json
import pprint as pp
from datetime import datetime
import re

def get_suggestions(search_string: str):
    base_url = 'https://utility.arcgis.com/usrsvcs/servers/b89ba3a68c664268b9bdea76948b4f11/rest/services/World/GeocodeServer/suggest'
    query_data = {
        'f': 'json',
        'text': search_string,
        'maxSuggestions': 60,
        'countryCode': 'BRA'
    }
    req = requests.get(base_url, params=query_data)
    res = json.loads(req.content)

    valid_suggestions = list(filter(lambda x: 'Curitiba' in x['text'], res['suggestions']))

    return valid_suggestions

def find_adress_candidates(single_line: str):
    base_url = 'https://utility.arcgis.com/usrsvcs/servers/b89ba3a68c664268b9bdea76948b4f11/rest/services/World/GeocodeServer/findAddressCandidates'
    query_data = {
        'f': 'json',
        'outSR': json.dumps({
            'wkid': 102100
        }),
        'outFields': '*',
        'countryCode': 'BRA',
        'maxLocations': 6,
        'SingleLine': single_line
    }

    req = requests.get(base_url, params=query_data)
    res = json.loads(req.content)

    return res

def get_codope(geometry):
    base_url = 'https://services1.arcgis.com/46Oage49MS2a3O6A/arcgis/rest/services/Mapa_Rodizio_Abastecimento_RMC_View/FeatureServer/1/query'
    query_data = {
        'f': 'json',
        'returnGeometry': False,
        'geometryType': 'esriGeometryPoint',
        'outFields': '*',
        'geometry': json.dumps(geometry)
    }

    req = requests.get(base_url, params=query_data)
    res = json.loads(req.content)

    codope = res['features'][0]['attributes']['codope']

    return codope

def get_by_codope(codope: str):
    base_url = 'https://services1.arcgis.com/46Oage49MS2a3O6A/arcgis/rest/services/Mapa_Rodizio_Abastecimento_RMC_View/FeatureServer/2/query'
    query_data = {
        'f': 'json',
        'returnGeometry': False,
        'outFields': '*',
        'where': "(CODOPE = '{}')".format(codope)
    }

    req = requests.get(base_url, params=query_data)
    res = json.loads(req.content)

    return res

def print_time_stamps(query_data):
    data = query_data['features']
    for entry in data:
        entry = entry['attributes']
        ts_retomada = datetime.fromtimestamp(entry['RETOMADA'] / 1000)
        ts_norm = datetime.fromtimestamp(entry['NORMALIZACAO'] / 1000)
        ts_init = datetime.fromtimestamp(entry['INICIO'] / 1000)

        print('INICIO: {} / NORMALIZACAO: {} / RETOMADA: {}'.format(ts_init, ts_norm, ts_retomada))

if __name__ == '__main__':
    suggestions = get_suggestions(search_string='Jose Clementino Bettega')
    # pp.pprint(suggestions)

    single_line = suggestions[0]['text']
    res = find_adress_candidates(single_line=single_line)

    geometry = res['candidates'][0]['location']
    geometry['spatialReference'] = {
        'wkid': 102100
    }

    codope = get_codope(geometry)
    res = get_by_codope(codope)

    print('Results query by codope')
    pp.pprint(res)

    # print_time_stamps(res)
    print('CODOPE: {}'.format(codope))

    # pp.pprint(res)


from __future__ import print_function

import os.path
import sys
import densenet
import numpy as np
import sklearn.metrics as metrics

from keras.datasets import cifar10
from keras.utils import np_utils
from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import Adam
from keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, TensorBoard
from keras import backend as K

batch_size = 100
nb_classes = 10
nb_epoch = 40

img_rows, img_cols = 32, 32
img_channels = 3

img_dim = (img_channels, img_rows, img_cols) if K.image_dim_ordering() == "th" else (img_rows, img_cols, img_channels)
depth = 40
nb_dense_block = 3
growth_rate = 12
nb_filter = -1
dropout_rate = 0.0  # 0.0 for data augmentation
if len(sys.argv) > 1:
    augment = sys.argv[1]
else:
    augment = 'false'

(trainX, trainY), (testX, testY) = cifar10.load_data()

trainX = trainX.astype('float32')
testX = testX.astype('float32')

trainX = densenet.preprocess_input(trainX)
testX = densenet.preprocess_input(testX)

Y_train = np_utils.to_categorical(trainY, nb_classes)
Y_test = np_utils.to_categorical(testY, nb_classes)

# GENERATOR
generator = ImageDataGenerator(rotation_range=15,
                               width_shift_range=5. / 32,
                               height_shift_range=5. / 32,
                               horizontal_flip=True)

generator.fit(trainX, seed=0)

# MODELS

model2k = densenet.DenseNet(img_dim, classes=nb_classes, depth=depth, nb_dense_block=nb_dense_block,
                            growth_rate=growth_rate, nb_filter=nb_filter, dropout_rate=dropout_rate, weights=None,
                            bottleneck=True, growth_rate_factor=2)

print("Models created")

# 2K MODEL
print("Building model 2k...")
model2k.summary()
optimizer = Adam(lr=1e-3)  # Using Adam instead of SGD to speed up training
model2k.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=["accuracy"])
print("Finished compiling")

# Load model
weights_file_2k = "weights/DenseNet-40-12-CIFAR10-2K.h5"
if os.path.exists(weights_file_2k):
    # model.load_weights(weights_file, by_name=True)
    print("Model loaded.")

out_dir = "weights/"
tb_dir_2k = "tensorboard/2k"

lr_reducer = ReduceLROnPlateau(monitor='val_acc', factor=np.sqrt(0.1),
                               cooldown=0, patience=5, min_lr=1e-5, verbose=1)
model_checkpoint_2k = ModelCheckpoint(weights_file_2k, monitor="val_acc", save_best_only=True,
                                      save_weights_only=True, verbose=1)
# tensorboard_2k = TensorBoard(log_dir=tb_dir_2k+"/logs", histogram_freq=5, batch_size=batch_size, write_graph=False,
#                              write_images=True, write_grads=True)

callbacks_2k = [lr_reducer, model_checkpoint_2k]
try:
    if augment == 'true':
        print("Training with data augmentation...")
        model2k.fit_generator(generator.flow(trainX, Y_train, batch_size=batch_size),
                              steps_per_epoch=len(trainX) // batch_size, epochs=nb_epoch,
                              callbacks=callbacks_2k,
                              validation_data=(testX, Y_test),
                              validation_steps=testX.shape[0] // batch_size, verbose=1)
    else:
        print("Training without data augmentation...")
        model2k.fit(trainX, Y_train, batch_size=batch_size, epochs=nb_epoch, callbacks=callbacks_2k,
                    validation_data=(testX, Y_test), verbose=2)
except KeyboardInterrupt:
    print("Training interrupted")
    sys.exit(1)

yPreds_2k = model2k.predict(testX)
yPred_2k = np.argmax(yPreds_2k, axis=1)
yTrue = testY

accuracy_2k = metrics.accuracy_score(yTrue, yPred_2k) * 100
error_2k = 100 - accuracy_2k
print("2K Accuracy : ", accuracy_2k)
print("2K Error : ", error_2k)
del model2k

# 4K MODEL
model4k = densenet.DenseNet(img_dim, classes=nb_classes, depth=depth, nb_dense_block=nb_dense_block,
                            growth_rate=growth_rate, nb_filter=nb_filter, dropout_rate=dropout_rate, weights=None,
                            bottleneck=True, growth_rate_factor=4)
print("Building model 4k...")
model4k.summary()
optimizer = Adam(lr=1e-3)  # Using Adam instead of SGD to speed up training
model4k.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=["accuracy"])
print("Finished compiling")

# Load model
weights_file_4k = "weights/DenseNet-40-12-CIFAR10-4K.h5"
if os.path.exists(weights_file_4k):
    # model.load_weights(weights_file, by_name=True)
    print("Model loaded.")

out_dir = "weights/"
tb_dir_4k = "tensorboard/4k"

lr_reducer = ReduceLROnPlateau(monitor='val_acc', factor=np.sqrt(0.1),
                               cooldown=0, patience=5, min_lr=1e-5, verbose=1)
model_checkpoint_4k = ModelCheckpoint(weights_file_4k, monitor="val_acc", save_best_only=True,
                                      save_weights_only=True, verbose=1)

# tensorboard_4k = TensorBoard(log_dir=tb_dir_4k+"/logs", histogram_freq=5, batch_size=batch_size, write_graph=False,
#                              write_images=True, write_grads=True)

callbacks_4k = [lr_reducer, model_checkpoint_4k]
try:
    if augment == 'true':
        print("Training with data augmentation...")
        model4k.fit_generator(generator.flow(trainX, Y_train, batch_size=batch_size),
                              steps_per_epoch=len(trainX) // batch_size, epochs=nb_epoch,
                              callbacks=callbacks_4k,
                              validation_data=(testX, Y_test),
                              validation_steps=testX.shape[0] // batch_size, verbose=1)
    else:
        print("Training without data augmentation...")
        model4k.fit(trainX, Y_train, batch_size=batch_size, epochs=nb_epoch, callbacks=callbacks_4k,
                    validation_data=(testX, Y_test), verbose=2)
except KeyboardInterrupt:
    print("Training interrupted")
    sys.exit(1)

yPreds_4k = model4k.predict(testX)
yPred_4k = np.argmax(yPreds_4k, axis=1)
yTrue = testY

accuracy_4k = metrics.accuracy_score(yTrue, yPred_4k) * 100
error_4k = 100 - accuracy_4k
print("4K Accuracy : ", accuracy_4k)
print("4K Error : ", error_4k)
del model4k

# 6K MODEL
print("Building model 6k...")

model6k = densenet.DenseNet(img_dim, classes=nb_classes, depth=depth, nb_dense_block=nb_dense_block,
                            growth_rate=growth_rate, nb_filter=nb_filter, dropout_rate=dropout_rate, weights=None,
                            bottleneck=True, growth_rate_factor=6)

model6k.summary()
optimizer = Adam(lr=1e-3)  # Using Adam instead of SGD to speed up training
model6k.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=["accuracy"])
print("Finished compiling")

# Load model
weights_file_6k = "weights/DenseNet-40-12-CIFAR10-6K.h5"
if os.path.exists(weights_file_6k):
    # model.load_weights(weights_file, by_name=True)
    print("Model loaded.")

out_dir = "weights/"
tb_dir_6k = "tensorboard/6k"

model_checkpoint_6k = ModelCheckpoint(weights_file_6k, monitor="val_acc", save_best_only=True,
                                      save_weights_only=True, verbose=1)

# tensorboard_6k = TensorBoard(log_dir=tb_dir_6k+"/logs", histogram_freq=5, batch_size=batch_size, write_graph=False,
#                              write_images=True, write_grads=True)

callbacks_6k = [lr_reducer, model_checkpoint_6k]
try:
    if augment == 'true':
        print("Training with data augmentation...")
        model6k.fit_generator(generator.flow(trainX, Y_train, batch_size=batch_size),
                              steps_per_epoch=len(trainX) // batch_size, epochs=nb_epoch,
                              callbacks=callbacks_6k,
                              validation_data=(testX, Y_test),
                              validation_steps=testX.shape[0] // batch_size, verbose=1)
    else:
        print("Training without data augmentation...")
        model6k.fit(trainX, Y_train, batch_size=batch_size, epochs=nb_epoch, callbacks=callbacks_6k,
                    validation_data=(testX, Y_test), verbose=2)
except KeyboardInterrupt:
    print("Training interrupted")
    sys.exit(1)

yPreds_6k = model6k.predict(testX)
yPred_6k = np.argmax(yPreds_6k, axis=1)

accuracy_6k = metrics.accuracy_score(yTrue, yPred_6k) * 100
error_6k = 100 - accuracy_6k
print("6K Accuracy : ", accuracy_6k)
print("6K Error : ", error_6k)
del model6k

# 8K MODEL
print("Building model 8k...")

model8k = densenet.DenseNet(img_dim, classes=nb_classes, depth=depth, nb_dense_block=nb_dense_block,
                            growth_rate=growth_rate, nb_filter=nb_filter, dropout_rate=dropout_rate, weights=None,
                            bottleneck=True, growth_rate_factor=8)

model8k.summary()
optimizer = Adam(lr=1e-3)  # Using Adam instead of SGD to speed up training
model8k.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=["accuracy"])
print("Finished compiling")

# Load model
weights_file_8k = "weights/DenseNet-40-12-CIFAR10-8K.h5"
if os.path.exists(weights_file_8k):
    # model.load_weights(weights_file, by_name=True)
    print("Model loaded.")

out_dir = "weights/"
tb_dir_8k = "tensorboard/8k"

model_checkpoint_8k = ModelCheckpoint(weights_file_8k, monitor="val_acc", save_best_only=True,
                                      save_weights_only=True, verbose=1)

# tensorboard_8k = TensorBoard(log_dir=tb_dir_8k+"/logs", histogram_freq=5, batch_size=batch_size, write_graph=False,
#                              write_images=True, write_grads=True)

callbacks_8k = [lr_reducer, model_checkpoint_8k]
try:
    if augment == 'true':
        print("Training with data augmentation...")
        model8k.fit_generator(generator.flow(trainX, Y_train, batch_size=batch_size),
                              steps_per_epoch=len(trainX) // batch_size, epochs=nb_epoch,
                              callbacks=callbacks_8k,
                              validation_data=(testX, Y_test),
                              validation_steps=testX.shape[0] // batch_size, verbose=1)
    else:
        print("Training without data augmentation...")
        model8k.fit(trainX, Y_train, batch_size=batch_size, epochs=nb_epoch, callbacks=callbacks_8k,
                    validation_data=(testX, Y_test), verbose=2)
except KeyboardInterrupt:
    print("Training interrupted")
    sys.exit(1)

yPreds_8k = model8k.predict(testX)
yPred_8k = np.argmax(yPreds_8k, axis=1)

accuracy_8k = metrics.accuracy_score(yTrue, yPred_8k) * 100
error_8k = 100 - accuracy_8k
print("8K Accuracy : ", accuracy_8k)
print("8K Error : ", error_8k)
del model8k


import torch
import torch.nn as nn
import torch.nn.functional as F
import logging
import collections
from util import compute_aggreeings, AverageMeter, get_mask, mask_tokens

def eval(model, val_loader, a2v, args, test=False):
    model.eval()
    count = 0
    metrics, counts = collections.defaultdict(int), collections.defaultdict(int)

    with torch.no_grad():
        if not args.mc:
            model.module._compute_answer_embedding(a2v)
        for i, batch in enumerate(val_loader):
            answer_id, answer, video, question = (
                batch["answer_id"],
                batch["answer"],
                batch["video"].cuda(),
                batch["question"].cuda(),
            )
            video_len = batch["video_len"]
            question_mask = (question > 0).float()
            video_mask = get_mask(video_len, video.size(1)).cuda()
            count += answer_id.size(0)

            if not args.mc:
                predicts = model(
                    video,
                    question,
                    text_mask=question_mask,
                    video_mask=video_mask,
                )
                topk = torch.topk(predicts, dim=1, k=10).indices.cpu()
                if args.dataset != "ivqa":
                    answer_id_expanded = answer_id.view(-1, 1).expand_as(topk)
                else:
                    answer_id = (answer_id / 2).clamp(max=1)
                    answer_id_expanded = answer_id
                metrics = compute_aggreeings(
                    topk,
                    answer_id_expanded,
                    [1, 10],
                    ["acc", "acc10"],
                    metrics,
                    ivqa=(args.dataset == "ivqa"),
                )
            else:
                fusion_proj, answer_proj = model(
                    video,
                    question,
                    text_mask=question_mask,
                    video_mask=video_mask,
                    answer=answer.cuda(),
                )
                fusion_proj = fusion_proj.unsqueeze(2)
                predicts = torch.bmm(answer_proj, fusion_proj).squeeze()
                predicted = torch.max(predicts, dim=1).indices.cpu()
                metrics["acc"] += (predicted == answer_id).sum().item()

    step = "val" if not test else "test"
    for k in metrics:
        v = metrics[k] / count
        logging.info(f"{step} {k}: {v:.2%}")

    return metrics["acc"] / count

def train(model, train_loader, a2v, optimizer, criterion, scheduler, epoch, args):
    model.train()
    running_vqa_loss, running_acc, running_mlm_loss = (
        AverageMeter(),
        AverageMeter(),
        AverageMeter(),
    )
    for i, batch in enumerate(train_loader):
        answer_id, answer, video, question = (
            batch["answer_id"],
            batch["answer"],
            batch["video"].cuda(),
            batch["question"].cuda(),
        )
        video_len = batch["video_len"]
        question_mask = (question > 0).float()
        video_mask = (
            get_mask(video_len, video.size(1)).cuda() if args.max_feats > 0 else None
        )
        N = answer_id.size(0)
        if not args.mc:
            model.module._compute_answer_embedding(a2v)
            predicts = model(
                video,
                question,
                text_mask=question_mask,
                video_mask=video_mask,
            )
        else:
            fusion_proj, answer_proj = model(
                video,
                question,
                text_mask=question_mask,
                video_mask=video_mask,
                answer=answer.cuda(),
            )
            fusion_proj = fusion_proj.unsqueeze(2)
            predicts = torch.bmm(answer_proj, fusion_proj).squeeze()

        if args.dataset == "ivqa":
            a = (answer_id / 2).clamp(max=1).cuda()
            vqa_loss = criterion(predicts, a)
            predicted = torch.max(predicts, dim=1).indices.cpu()
            predicted = F.one_hot(predicted, num_classes=len(a2v))
            running_acc.update((predicted * a.cpu()).sum().item() / N, N)
        else:
            vqa_loss = criterion(predicts, answer_id.cuda())
            predicted = torch.max(predicts, dim=1).indices.cpu()
            running_acc.update((predicted == answer_id).sum().item() / N, N)

        if args.mlm_prob:
            inputs = batch["question"]
            inputs, labels = mask_tokens(
                inputs, model.module.bert.bert_tokenizer, mlm_probability=0.15
            )
            mlm_loss = model(
                video,
                question=inputs.cuda(),
                labels=labels.cuda(),
                text_mask=question_mask,
                video_mask=video_mask,
                mode="mlm",
            )
            mlm_loss = mlm_loss.mean()
            loss = mlm_loss + vqa_loss
        else:
            loss = vqa_loss

        optimizer.zero_grad()
        loss.backward()
        if args.clip:
            nn.utils.clip_grad_norm_(model.parameters(), max_norm=args.clip)
        optimizer.step()
        scheduler.step()

        running_vqa_loss.update(vqa_loss.detach().cpu().item(), N)
        if args.mlm_prob:
            running_mlm_loss.update(mlm_loss.detach().cpu().item(), N)
        if (i + 1) % (len(train_loader) // args.freq_display) == 0:
            if args.mlm_prob:
                logging.info(
                    f"Epoch {epoch + 1}, Epoch status: {float(i + 1) / len(train_loader):.4f}, Training VideoQA loss: "
                    f"{running_vqa_loss.avg:.4f}, Training acc: {running_acc.avg:.2%}, Training MLM loss: {running_mlm_loss.avg:.4f}"
                )
            else:
                logging.info(
                    f"Epoch {epoch + 1}, Epoch status: {float(i + 1) / len(train_loader):.4f}, Training VideoQA loss: "
                    f"{running_vqa_loss.avg:.4f}, Training acc: {running_acc.avg:.2%}"
                )
            running_acc.reset()
            running_vqa_loss.reset()
            running_mlm_loss.reset()


import json

class Kasir:
    stokBarang = {}

    barangPembeli = {}

    def __init__(self):
        pass

    def simpanBeliJson(self):
        with open("beli.json", "w") as beliFileJson:
            json.dump(Kasir.barangPembeli, beliFileJson)

    def isiStok(self):
        with open("stok.json", "r") as stokReadJson:
            stokDict = json.load(stokReadJson)
            for key, value in stokDict.items():
                print("Barang :", key, "Harga:", value)

    def isiKranjang(self):
        print("Isi Belanjaan Anda: ")
        for key, value in Kasir.barangPembeli.items():
            print("BARANG \t:", key)
            for key2 in value:
                print(key2.upper(), "\t:", value[key2])

    def tambahStok(self):
        stokTrue = input("Apa Anda Belum Punya Database Stock?(y/t) ")
        keyStok = input("Masukkan Barang: ")
        valueStok = int(input("Masukkan Harga: "))
        if stokTrue.lower() == "y":
            Kasir.stokBarang.update({keyStok: valueStok})
            with open("stok.json", "w") as stokFileJson:
                json.dump(Kasir.stokBarang, stokFileJson)
                print("=== Database Stock Sudah diBuat!!!===")
        else:
            with open("stok.json", "r") as stokReadJson:
                stokDict = json.load(stokReadJson)
                if keyStok.lower() in stokDict.keys():
                    print("Barang Sudah Ada!!! \nTambahkan Barang Lain!!!")
                    Kasir.tambahStok(self)
                else:
                    stokDict.update({keyStok: valueStok})
                    with open("stok.json", "w") as stokFileJson:
                        json.dump(stokDict, stokFileJson)
                    print("=== Stock Berhasil Ditambahkan :) ===")

    def editStok(self):
        keyStokEdit = input("Masukkan Barang yang Dirubah: ")
        valueStokEdit = int(input("Masukkan Harga: "))
        with open("stok.json", "r") as stokReadJson:
            stokDict = json.load(stokReadJson)
            if keyStokEdit.lower() in stokDict.keys():
                stokDict.update({keyStokEdit: valueStokEdit})
                with open("stok.json", "w") as stokFileJson:
                    json.dump(stokDict, stokFileJson)
                print("=== Stock Berhasil DiRubah :) ===")
            else:
                print("===Stock Barang Tidak Ditemukan!!!===")
                print("===Edit Barang Yang Ada===!!!")
                Kasir.editStok(self)

    def hapusStok(self):
        keyStockHapus = input("Masukkan Nama Barang yang Dihapus: ")
        with open("stok.json", "r") as stokReadJson:
            stokDict = json.load(stokReadJson)
            if keyStockHapus.lower() in stokDict.keys():
                del stokDict[keyStockHapus]
                with open("stok.json", "w") as stokFileJson:
                    json.dump(stokDict, stokFileJson)
                print("=== Stock Berhasil Dihapus :) ===")
            else:
                print("===Stock Barang Tidak Ditemukan!!!===")
                print("===Hapus Barang Yang Ada===!!!")
                Kasir.hapusStok(self)

    def beliStock(self, keyStokBeli, jmlhStokBeli):
        with open("stok.json", "r") as stokReadJson:
            stokDict = json.load(stokReadJson)
            Kasir.barangPembeli.update(
                {keyStokBeli: {"jumlah": jmlhStokBeli, "harga": stokDict[keyStokBeli]}})
            print("===Belanja Ditambahkan Kekranjang===")
            Kasir.simpanBeliJson(self)

    def totalIsiKranjang(self):
        totalHarga = 0
        hargaKali = 0
        with open("beli.json", "r") as beliJson:
            stokDict = json.load(beliJson)
            for key, value in stokDict.items():
                harga = float(value["harga"])
                jumlah = int(value["jumlah"])
                hargaKali = harga * jumlah
                print(f"{key} \t: {harga} * {jumlah} = {hargaKali}")
                totalHarga += hargaKali
            print("Harga Total Barang: ", totalHarga)

    # testing fungstion

    def testTotalIsiKranjang(self):
        totalHarga = 0
        hargaKali = 0
        with open("beli.json", "r") as beliJson:
            stokDict = json.load(beliJson)
            for key, value in stokDict.items():
                harga = float(value["harga"])
                jumlah = int(value["jumlah"])
                hargaKali = harga * jumlah
                print(f"{key} \t: {harga} * {jumlah} = {hargaKali}")
                totalHarga += hargaKali
            return totalHarga

    def testIsiKranjang(self, cariVaribel):
        with open("beli.json", "r") as beliJson:
            stokDict = json.load(beliJson)
            if cariVaribel.lower() in stokDict.keys():
                return True
            else:
                return False

    def testIsiStok(self, cariVaribel):
        with open("stok.json", "r") as stokReadJson:
            stokDict = json.load(stokReadJson)
            if cariVaribel.lower() in stokDict.keys():
                return True
            else:
                return False


from datetime import datetime

from django.core.paginator import Paginator
from django.db.models import Q
from django.http import JsonResponse
from django.core.serializers import serialize
from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import CreateView, DeleteView, DetailView, ListView, UpdateView
from .models import MatsterApplication
from .forms import MasterForm
from Appartament.models import Appartament
from House.models import House
from User.models import User
# Create your views here.

class MasterList(ListView):
    model = MatsterApplication
    template_name = 'Master_application/master_list.html'
    queryset = MatsterApplication.objects.all()

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super(MasterList, self).get_context_data(**kwargs)
        context['owners'] = User.objects.all()
        context['masters'] = User.objects.filter(role__user__isnull=False).distinct()
        return context

class MasterCreate(CreateView):
    form_class = MasterForm
    template_name = 'Master_application/master_create.html'
    success_url = reverse_lazy('master_list')

    def get_context_data(self, **kwargs):
        context = super(MasterCreate, self).get_context_data(**kwargs)
        return context

class MasterUpdate(UpdateView):
    form_class = MasterForm
    model = MatsterApplication
    template_name = 'Master_application/master_update.html'
    success_url = reverse_lazy('master_list')

    def get_context_data(self, **kwargs):
        context = super(MasterUpdate, self).get_context_data(**kwargs)
        return context

    def form_valid(self, form):
        return super().form_valid(form=form)

class MasterDetail(DetailView):
    model = MatsterApplication
    template_name = 'Master_application/master_detail.html'

class MasterDelete(DeleteView):
    model = MatsterApplication
    success_url = reverse_lazy('master_list')

    def get(self, request, *args, **kwags):
        return self.delete(self, request, *args, *kwags)

def filter_master_appartament(request):
    print(request.GET.get('owner_id'))
    if request.GET.get('owner_id') != '' or request.GET.get('owner_id') is not None:
        appartaments = serialize('json', Appartament.objects.filter(owner_id=request.GET.get('owner_id')))
        houses = serialize('json', House.objects.all())
        return JsonResponse({'appartaments': appartaments, 'houses': houses}, status=200)

def master_appartament_list(request):
    masters = MatsterApplication.objects.all()

    #Search
    search_master_num = request.GET.get('master_num')
    search_master_time = request.GET.get('master_time')
    search_master_type = request.GET.get('master_type')
    search_master_description = request.GET.get('master_description')
    search_master_flat = request.GET.get('master_flat')
    search_master_owner = request.GET.get('master_owner')
    search_master_phone = request.GET.get('master_phone')
    search_master_name = request.GET.get('master_name')
    search_master_status = request.GET.get('master_status')

    query = Q()

    if search_master_num:
        query &= Q(id__icontains=search_master_num)

    if search_master_time:
        start = datetime.strptime(search_master_time[0:10], '%d.%m.%Y').date()
        end = datetime.strptime(search_master_time[13:23], '%d.%m.%Y').date()
        print()
        print('date:', start, '+', end)
        print()
        query &= Q(date_master__gte=start.strftime('%Y-%m-%d'), date_master__lte=end.strftime('%Y-%m-%d'))

    if search_master_type:
        query &= Q(typeMaster__name=search_master_type)

    if search_master_description:
        query &= Q(description_problem__icontains=search_master_description)

    if search_master_flat:
        query &= Q(appartament__number_appartament__icontains=search_master_flat)

    if search_master_owner:
        query &= Q(ownerAppartament=search_master_owner)

    if search_master_phone:
        query &= Q(name_master__phone_number__icontains=search_master_phone)

    if search_master_name:
        query &= Q(name_master_id=search_master_name)

    if search_master_status:
        query &= Q(status=search_master_status)

    masters = masters.filter(query)

    start = int(request.GET.get('start', 0))
    length = int(request.GET.get('length', 10))

    paginator = Paginator(masters, length)
    masters = paginator.get_page(start // length + 1)

    data = []

    for master in masters:
        data.append({
            'id': master.id,
            'time': f'{master.date_master.strftime("%d.%m.%Y")}-{master.time_master.strftime("%H:%M")}',
            'type': master.typeMaster.name,
            'description': master.description_problem,
            'flat': master.appartament.number_appartament,
            'owner': master.ownerAppartament.get_full_name(),
            'phone': master.name_master.phone_number,
            'master': master.name_master.get_full_name(),
            'status': master.status
        })

    response = {
        'draw': request.GET.get('draw'),
        'recordsTotal': MatsterApplication.objects.all().count(),
        'recordsFiltered': masters.paginator.count,
        'data': data
    }

    return JsonResponse(response)


import glob
import argparse
import re
import os.path
import shutil
import sys
import tempfile
import itertools
import json

VALID_PROTOCOLS = ['git', 'ssh', 'http', 'https', 'ssh-colon', 'file', 'relative']
VERSION = '0.2.0'

parser = argparse.ArgumentParser(
        description = 'Recursively search directory for .git/config files and update all remote URIs',
        formatter_class = argparse.RawDescriptionHelpFormatter,
        epilog = '''
%(prog)s expects a JSON file containing search paramaters and a substitution
dictionary. A simple example:

{
  "search": {
     "hostname": "oldgit|oldgit.example.org",
     "path": "/+(?:var|srv)/+git"
   },
  "replace": {
    "hostname": "newgit.example.com",
    "username": "git",
    "protocol": "ssh-colon",
    "substitutions": {
      "oldproject1": "new/path/project1",
      "oldproject2": "oldproject2"
    }
  }
}

With this file search for .git/config files recursively and replace all
occurrences matching the provided search rules and replace the URLs.
.gitmodules can also be modified.

(Note that search.hostname and search.path are regular expressions, which have
to be escaped to be valid JSON. These expressions are part of a bigger
expression and must not contain anchors like '^' and '$'.)

%(prog)s provides several ways to test your rules before changing your files.
In all cases %(prog)s expects a JSON file as the first argument and one or
several files or directories. The files are expected to be .git/config files
(and/or .gitmodules files if --modules is given) and/or directories that will
be searched recursively for mentioned files.

%(prog)s --list-configs
  Simply list all the .git/config files found (with matching URIs inside)

%(prog)s --list-projects
  List all the project names matching the search criteria. If the project does
  not have a substitution rule, a warning will be issued.

%(prog)s --list-projects --list-categorised --show-new-path
  List all found projects, indented by config file name, along with the new
  name it will be substituted with (not the full URI, but the new path).

%(prog)s --dry-run
  Print the contents of all the files that would have been modified.

The resulting URI must be specified with hostname, username and protocol. All
of these can be overridden by the command line. When using the protocol
'relative' the hostname may be of the form '../..' and username is ignored.

TIP: Before changing .gitmodules files, it might be a good idea to run %(prog)s
--list-config --modules-only to get a list of the modified projects. This can
be handy for an automated script running 'git commit' of the changes later:
    %(prog)s --list-config config.json --modules-only my_path

This is done by the helper script 'list_affected_gitmodules.sh'. Another
script, commit_gitmodules_changes.sh, will add and commit all affected
.gitmodules files and push them to the remote 'origin'. The complete list of
actions for renaming all URIs in local .git/configs as well as updating all
submodules and committing and pushing the changes are as follows:
    # Do a dry run first:
    %(prog)s --modules --dry-run config.json my_paths
    # Store a list of the projects that will be changed and needs to be commited:
    /usr/share/doc/%(prog)s/scripts/list_affected_gitmodules.sh config.json my_paths > modified_projects
    # Replace URIs:
    %(prog)s --modules config.json my_paths
    # Add, commit and push changes to submodule URIs:
    /usr/share/doc/%(prog)s/scripts/commit_gitmodules_changes.sh modified_projects
        '''
        )
parser.add_argument('--list-configs', action = 'store_true', help = 'List all matching .git/config files and do nothing else')
parser.add_argument('--list-projects', action = 'store_true', help = 'List all projects found in all found .git/config files and do nothing else')
parser.add_argument('--show-new-path', const = ' → ', default = None, nargs = '?', help = 'When listing projects, also show their new paths, separated by a delimiter (default " → ")')
parser.add_argument('--list-categorised', action = 'store_true', help = 'When listing projects, print them indented under the config file they are found in')
parser.add_argument('--dry-run', '-d', action = 'store_true', help = 'Instead of substituting paths just print what would have been done (same as --list-projects --show-new-path --list-categorised')
parser.add_argument('--protocol', '-p', choices = VALID_PROTOCOLS, help = 'Protocol to use in the new URI, default ssh (overrides config)')
parser.add_argument('--username', '-u', help = 'Username to use in the new URI (overrides config)')
parser.add_argument('--modules', '-m', action = 'store_true', help = 'Replace/inspect URIs in .gitmodules files as well')
parser.add_argument('--modules-only', action = 'store_true', help = 'Replace/inspect URIs in .gitmodules files only')
parser.add_argument('--hostname', help = 'Hostname in the new URI (overrides config)')
parser.add_argument('--version', action='version', version='%(prog)s ' + VERSION)
parser.add_argument('config', help = 'JSON configuration file')
parser.add_argument('targets', nargs = '+', metavar = 'file|directory', help = 'One or several directories containing git projects, or one or several git project files, to be updated')

def printIfMatching(gitConfigFile):
    with open(gitConfigFile, 'r') as config:
        if regex.search(config.read()):
            print(gitConfigFile)

def listProjects(gitConfigFile, subst, categorise = False, showNew = None):
    if categorise:
        print(gitConfigFile, ':', sep='')

    with open(gitConfigFile, 'r') as config:
        for match in regex.finditer(config.read()):
            project = match.group('project')
            try:
                newName = subst[project]
            except KeyError :
                print('WARNING: The project "{0}" does not have a defined new path'.format(project), file = sys.stderr)
                continue

            if categorise:
                print('\t', end='')
            if showNew is not None:
                print('{0}{1}{2}'.format(project, showNew, newName))
            else:
                print(match.group('project'))

def replace(gitConfigFile, subst, host, proto, username = None, dryRun = False):
    if dryRun:
        print('In {0} the following would be the new contents:'.format(gitConfigFile))

    (_, tempFile) = tempfile.mkstemp()
    try:
        with open(gitConfigFile, 'r') as configOrig, open(tempFile, 'w') as temp:
            def replace(match):
                project = match.group('project')
                try:
                    newName = subst[project]
                except KeyError :
                    print('WARNING: The project "{0}" does not have a defined new path and will not be replaced'.format(project), file = sys.stderr)
                    if not dryRun:
                        temp.write(match.string)

                    return match.string

                user = username + '@' if username is not None else ''
                if proto == 'ssh-colon':
                    uri = '{user}{host}:{path}'.format(
                        user = user,
                        host = host,
                        path = newName
                        )
                elif proto == 'relative':
                    uri = '{host}/{path}'.format(
                            host = host,
                            path = newName
                            )
                else:
                    uri = '{proto}://{user}{host}/{path}'.format(
                        proto = proto,
                        user = user,
                        host = host,
                        path = newName
                        )

                return match.group('key') + uri

            result = regex.sub(lambda match: replace(match), configOrig.read())
            if dryRun:
                print(result)
            else:
                temp.write(result)

    except IOError as e:
        print('Failed to replace config / creating temporary file {}: {}'.format(e.filename, e.strerror), file = sys.stderr)
        try:
            os.remove(tempFile)
        except IOError:
            pass
    else:
        if not dryRun:
            shutil.move(tempFile, gitConfigFile)

def confFiles(path, includeGitModules = False, gitModulesOnly = False):
    if gitModulesOnly:
        return glob.iglob(target + '/**/.gitmodules', recursive = True)
    elif includeGitModules:
        return itertools.chain(
                glob.iglob(target + '/**/.git/config', recursive = True),
                glob.iglob(target + '/**/.git/modules/**/config', recursive = True),
                glob.iglob(target + '/**/.gitmodules', recursive = True)
                )
    else:
        return itertools.chain(
                glob.iglob(target + '/**/.git/config', recursive = True),
                glob.iglob(target + '/**/.git/modules/**/config', recursive = True)
                )

def validateJSONConfig(json):
    if 'protocol' in json['replace'] and json['replace']['protocol'] not in VALID_PROTOCOLS:
        raise ValueError('replace/protocol "{}" is invalid (valid protocols: {})'
                .format(json['replace']['protocol'], ', '.join(VALID_PROTOCOLS)))

    try:
        re.compile(json['search']['hostname'])
        if 'path' in json['search']:
            re.compile(json['search']['path'])
    except re.error as e:
        print('Config contains invalid regex "{}": {}'.format(e.pattern, e.msg), file = sys.stderr)

if __name__ == '__main__':
    args = parser.parse_args()

    with open(args.config) as confFile:
        json = json.load(confFile)

    validateJSONConfig(json)

    if 'path' in json['search']:
        # TODO: relative path is way to simplified:
        regex = re.compile("""
        ^(?P<key>\s*url\s*=\s*)
        (?:
        # Either an URI:
          (?:(file|ssh|git|http|https)://)?         # protocol, optional
          (?:[a-z_][a-z0-9_-]*[$]?@)??              # Linux username, followed by '@', optional
          (?:{hosts})                               # Accepted hostnames
          :?(?:{paths})                             # Common paths
        | # or a relative path
          \.\.)
        /+(?P<project>[^\.\n]+)                     # project name
        (?:\.git)??                                 # optionally followed by ".git"
        \s*$""".format(
            hosts = json['search']['hostname'],
            paths = json['search']['path']), re.MULTILINE | re.VERBOSE)
    else:
        regex = re.compile("""
        ^(?P<key>\s*url\s*=\s*)
        (?:(file|ssh|git|http|https)://)?           # protocol, optional
        (?:[a-z_][a-z0-9_-]*[$]?@)??                # Linux username, followed by '@', optional
        (?:{hosts})                                 # Accepted hostnames
        :?
        (?P<project>[^\.\n]+)                       # project path/name
        (?:\.git)??                                 # optionally followed by ".git"
        \s*$""".format(hosts = json['search']['hostname']), re.MULTILINE | re.VERBOSE)

    for target in args.targets:
        if os.path.isdir(target):
            for config in confFiles(target, args.modules, args.modules_only):
                if args.list_configs:
                    printIfMatching(config)
                elif args.list_projects:
                    listProjects(
                            config,
                            subst = json['replace']['substitutions'],
                            categorise = args.list_categorised,
                            showNew = args.show_new_path
                            )
                else:
                    replace(
                            config,
                            subst = json['replace']['substitutions'],
                            host = args.hostname if args.hostname else json['replace']['hostname'],
                            proto = args.protocol if args.protocol else json['replace']['protocol'],
                            username = args.username if args.username else json['replace'].get('username'),
                            dryRun = args.dry_run
                            )
        else:
            if args.list_configs:
                printIfMatching(target)
            elif args.list_projects:
                listProjects(
                        target,
                        subst = json['replace']['substitutions'],
                        categorise = args.list_categorised,
                        showNew = args.show_new_path
                        )
            else:
                    replace(
                            target,
                            subst = json['replace']['substitutions'],
                            host = args.hostname if args.hostname else json['replace']['hostname'],
                            proto = args.protocol if args.protocol else json['replace']['protocol'],
                            username = args.username if args.username else json['replace'].get('username'),
                            dryRun = args.dry_run
                            )


# -*- coding: utf-8 -*-
__doc__ == """
Simple view that exposes a REST API over HTTP
to retrieve the least of last read books from
a bookshelf.

To run it:

.. code-block:: python

   $ python last_read.py --topic mytopic --broker <BROKER_ADDR>:9092 --port 8080 --name lastread --id lastread1 --tags book last

This will listen for HTTP request on `127.0.0.1:8080`
and will return a JSON encoded list of book
documents.

The service will be automatically registered to the local consul
agent (assuming running localhost:8500) using the
name, id and tags provided

When the process is terminated, the service is deregistered
automatically.
"""
import asyncio
import json
import signal

from aiohttp import web

from discolib import register_service, deregister_service
from eventlib import consume_events, stop_consuming_events
from restlib import webserver, route_to_resource
from utils import get_cli_parser

# internal state
bookshelf = []
loop = asyncio.get_event_loop()

async def bookshelf_view(request):
    """
    View to see the current list of books
    in your bookshelf.
    """
    return web.Response(body=json.dumps(bookshelf).encode('utf-8'),
                        headers={"content-type": "application/json"})

async def event_handler(message):
    """
    Called whenever a new event was received from
    the event store.

    Simply store the event in a local state arena.
    """
    bookshelf.append(message.value.decode('utf-8'))

def run():
    """
    Entry point to this microservice.
    """
    args = get_cli_parser().parse_args()

    # schedule the internal event consumer
    # that will run until we terminate this service
    asyncio.ensure_future(consume_events(topic=args.topic.encode('utf-8'),
                                         group=args.group,
                                         addr=args.broker,
                                         callback=event_handler))

    # let's start the REST server that will
    # serve the view's resource
    srv = loop.run_until_complete(webserver(args.addr, args.port))
    loop.run_until_complete(route_to_resource(bookshelf_view))

    # finally, let's advertize this service
    # to the rest of the world
    svc = loop.run_until_complete(register_service(
            id=args.id,
            name=args.name,
            port=args.port,
            address=args.addr,
            tags=args.tags
        )
    )

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        loop.run_until_complete(deregister_service(svc))
        loop.run_until_complete(stop_consuming_events())

        srv.close()
        loop.run_until_complete(srv.wait_closed())
        # give the time for remaining requests to complete
        loop.run_until_complete(asyncio.sleep(2))

    loop.close()

if __name__ == '__main__':  # pragma: no cover
    run()


from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^$', views.dashboard, name='dashboard'),
    url(r'^add_mentor$', views.add_mentor, name='add_mentor'),
    url(r'^add_investor$', views.add_investor, name='add_investor'),

    url(r'^show_startups$', views.show_startups, name='show_startups'),
    url(r'^show_investors$', views.show_investors, name='show_investors'),
    url(r'^show_mentors$', views.show_mentors, name='show_mentors'),
    #url(r'^test$', views.test),
    url(r'^upload_documents$', views.upload_documents, name='upload_documents'),
    url(r'^update_info$', views.update_info, name='update_info'),
    url(r'^show_tickets$', views.show_tickets, name='show_tickets'),
    url(r'^show_incubation$', views.show_incubation, name='show_incunbation'),
    url(r'^accept_fund/(?P<pk>[0-9]+)$',views.accept_fund,name='accept_fund'),
    url(r'^reject_fund/(?P<pk>[0-9]+)$',views.reject_fund,name='reject_fund'),
    url(r'^accept_incubation/(?P<pk>[0-9]+)$',views.accept_incubation,name='accept_incubation'),
    url(r'^reject_incubation/(?P<pk>[0-9]+)$',views.reject_incubation,name='reject_incubation'),
    url(r'^show_ticket/(?P<pk>[0-9]+)$',views.show_ticket,name='show_ticket'),
    url(r'^solve_ticket/(?P<pk>[0-9]+)$',views.solve_ticket,name='solve_ticket'),
    url(r'^assign_mentor$', views.assign_mentor, name='assign_mentor'),
    url(r'^reviews$', views.reviews, name='reviews'),
    url(r'^set_milestone/(?P<pk>[0-9]+)$',views.set_milestone,name='set_milestone'),
    url(r'^show_milestone/(?P<pk>[0-9]+)$',views.show_milestone,name='show_milestone'),
    url(r'^show_funded_startups$', views.show_funded_startups, name='show_funded_startups'),
    url(r'^complete_milestone/(?P<pk>[0-9]+)$',views.complete_milestone,name='complete_milestone'),

]


import yaml

from modulemd.components import ModuleComponents
from modulemd.content import ModuleContent
from modulemd.rpms import ModuleRPMs
from modulemd.profile import ModuleProfile

supported_mdversions = ( 0, )

class ModuleMetadata(object):
    """Class representing the whole module."""

    REPODATA_FILENAME = "modulemd"

    def __init__(self):
        """Creates a new ModuleMetadata instance."""
        self.mdversion = max(supported_mdversions)
        self.name = ""
        self.version = ""
        self.release = ""
        self.summary = ""
        self.description = ""
        self.module_licenses = set()
        self.content_licenses = set()
        self.buildrequires = dict()
        self.requires = dict()
        self.community = ""
        self.documentation = ""
        self.tracker = ""
        self.xmd = dict()
        self.profiles = dict()
        self.components = None

    def load(self, f):
        """Loads a metadata file into the instance.

        :param str f: File name to load
        """
        with open(f, "r") as infile:
            data = infile.read()
        self.loads(data)

    def loads(self, s):
        """Loads metadata from a string.

        :param str s: Raw metadata in YAML
        :raises ValueError: If the metadata is invalid or unsupported.
        """
        yml = yaml.safe_load(s)
        if "document" not in yml or yml["document"] != "modulemd":
            raise ValueError("The supplied data isn't a valid modulemd document")
        if "version" not in yml:
            raise ValueError("version is required")
        if yml["version"] not in supported_mdversions:
            raise ValueError("The supplied metadata version isn't supported")
        self.mdversion = yml["version"]
        if "data" not in yml or not isinstance(yml["data"], dict):
            return
        if "name" in yml["data"]:
            self.name = yml["data"]["name"]
        if "version" in yml["data"]:
            self.version = yml["data"]["version"]
        if "release" in yml["data"]:
            self.release = yml["data"]["release"]
        if "summary" in yml["data"]:
            self.summary = yml["data"]["summary"]
        if "description" in yml["data"]:
            self.description = str(yml["data"]["description"]).strip()
        if ("license" in yml["data"]
                and isinstance(yml["data"]["license"], dict)
                and "module" in yml["data"]["license"]
                and yml["data"]["license"]["module"]):
            self.module_licenses = set(yml["data"]["license"]["module"])
        if ("license" in yml["data"]
                and isinstance(yml["data"]["license"], dict)
                and "content" in yml["data"]["license"]):
            self.content_licenses = set(yml["data"]["license"]["content"])
        if ("dependencies" in yml["data"]
                and isinstance(yml["data"]["dependencies"], dict)):
            if ("buildrequires" in yml["data"]["dependencies"]
                    and yml["data"]["dependencies"]["buildrequires"]):
                self.buildrequires = yml["data"]["dependencies"]["buildrequires"]
            if ("requires" in yml["data"]["dependencies"]
                    and yml["data"]["dependencies"]["requires"]):
                self.requires = yml["data"]["dependencies"]["requires"]
        if "references" in yml["data"] and yml["data"]["references"]:
            if "community" in yml["data"]["references"]:
                self.community = yml["data"]["references"]["community"]
            if "documentation" in yml["data"]["references"]:
                self.documentation = yml["data"]["references"]["documentation"]
            if "tracker" in yml["data"]["references"]:
                self.tracker = yml["data"]["references"]["tracker"]
        if "xmd" in yml["data"]:
            self.xmd = yml["data"]["xmd"]
        if ("profiles" in yml["data"]
                and isinstance(yml["data"]["profiles"], dict)):
            for profile in yml["data"]["profiles"].keys():
                self.profiles[profile] = ModuleProfile()
                if "description" in yml["data"]["profiles"][profile]:
                    self.profiles[profile].description = \
                        str(yml["data"]["profiles"][profile]["description"])
                if "rpms" in yml["data"]["profiles"][profile]:
                    self.profiles[profile].rpms = \
                        set(yml["data"]["profiles"][profile]["rpms"])
        if ("components" in yml["data"]
                and isinstance(yml["data"]["components"], dict)):
            self.components = ModuleComponents()
            if "rpms" in yml["data"]["components"]:
                self.components.rpms = ModuleRPMs()
                if "dependencies" in yml["data"]["components"]["rpms"]:
                    self.components.rpms.dependencies = \
                        yml["data"]["components"]["rpms"]["dependencies"]
                if "api" in yml["data"]["components"]["rpms"]:
                    self.components.rpms.api = \
                        set(yml["data"]["components"]["rpms"]["api"])
                if "packages" in yml["data"]["components"]["rpms"]:
                    for p, e in yml["data"]["components"]["rpms"]["packages"].items():
                        extras = dict()
                        extras["rationale"] = e["rationale"]
                        if "repository" in e:
                            extras["repository"] = e["repository"]
                        if "cache" in e:
                            extras["cache"] = e["cache"]
                        if "commit" in e:
                            extras["commit"] = e["commit"]
                        if "arches" in e:
                            extras["arches"] = e["arches"]
                        if "multilib" in e:
                            extras["multilib"] = e["multilib"]
                        self.components.rpms.add_package(p, **extras)
                if "filter" in yml["data"]["components"]["rpms"]:
                    self.components.rpms.filter = \
                        set(yml["data"]["components"]["rpms"]["filter"])

    def dump(self, f):
        """Dumps the metadata into the supplied file.

        :param str f: File name of the destination
        """
        data = self.dumps()
        with open(f, "w") as outfile:
            outfile.write(data)

    def dumps(self):
        """Dumps te metadata into a string.

        :rtype: str
        :raises Exception: If metadata validation fails
        """
        if not self.validate:
            raise Exception("Metadata validation failed")
        data = dict()
        # header
        data["document"] = "modulemd"
        data["version"] = self.mdversion
        # data
        data["data"] = dict()
        data["data"]["name"] = self.name
        data["data"]["version"] = self.version
        data["data"]["release"] = self.release
        data["data"]["summary"] = self.summary
        data["data"]["description"] = self.description
        data["data"]["license"] = dict()
        data["data"]["license"]["module"] = list(self.module_licenses)
        if self.content_licenses:
            data["data"]["license"]["content"] = list(self.content_licenses)
        if self.buildrequires or self.requires:
            data["data"]["dependencies"] = dict()
            if self.buildrequires:
                data["data"]["dependencies"]["buildrequires"] = self.buildrequires
            if self.requires:
                data["data"]["dependencies"]["requires"] = self.requires
        if self.community or self.documentation or self.tracker:
            data["data"]["references"] = dict()
            if self.community:
                data["data"]["references"]["community"] = self.community
            if self.documentation:
                data["data"]["references"]["documentation"] = self.documentation
            if self.tracker:
                data["data"]["references"]["tracker"] = self.tracker
        if self.xmd:
            data["data"]["xmd"] = self.xmd
        if self.profiles:
            data["data"]["profiles"] = dict()
            for profile in self.profiles.keys():
                if self.profiles[profile].description:
                    if profile not in data["data"]["profiles"]:
                        data["data"]["profiles"][profile] = dict()
                    data["data"]["profiles"][profile]["description"] = \
                        str(self.profiles[profile].description)
                if self.profiles[profile].rpms:
                    if profile not in data["data"]["profiles"]:
                        data["data"]["profiles"][profile] = dict()
                    data["data"]["profiles"][profile]["rpms"] = \
                        list(self.profiles[profile].rpms)
        if self.components:
            data["data"]["components"] = dict()
            if self.components.rpms:
                data["data"]["components"]["rpms"] = dict()
                data["data"]["components"]["rpms"]["dependencies"] = \
                    self.components.rpms.dependencies
                data["data"]["components"]["rpms"]["api"] = \
                    list(self.components.rpms.api)
                if self.components.rpms.packages:
                    data["data"]["components"]["rpms"]["packages"] = dict()
                    for p, e in self.components.rpms.packages.items():
                        extra = dict()
                        extra["rationale"] = e["rationale"]
                        if "commit" in e:
                            extra["commit"] = e["commit"]
                        if "repository" in e:
                            extra["repository"] = e["repository"]
                        if "cache" in e:
                            extra["cache"] = e["cache"]
                        if "arches" in e:
                            extra["arches"] = e["arches"]
                        if "multilib" in e:
                            extra["multilib"] = e["multilib"]
                        data["data"]["components"]["rpms"]["packages"][p] = \
                            extra
                if self.components.rpms.filter:
                    data["data"]["components"]["rpms"]["filter"] = \
                        list(self.components.rpms.filter)
        return yaml.safe_dump(data)

    def validate(self):
        """Performs an in-depth validation of the metadata instance.

        :rtype: bool
        :raises TypeError: If properties are holding data of incorrect type
        :raises ValueError: If properties are holding invalid data
        """
        if not isinstance(self.mdversion, int):
            raise TypeError("mdversion must be an integer")
        if not isinstance(self.name, str):
            raise TypeError("name must be a string")
        if not isinstance(self.version, str):
            raise TypeError("version must be a string")
        if not isinstance(self.release, str):
            raise TypeError("release must be a string")
        if not isinstance(self.summary, str):
            raise TypeError("summary must be a string")
        if not isinstance(self.description, str):
            raise TypeError("description must be a string")
        if not isinstance(self.module_licenses, set):
            raise TypeError("module_licenses must be a set")
        for l in self.module_licenses:
            if not isinstance(l, str):
                raise TypeError("module_licenses must be a set of strings")
        if not isinstance(self.content_licenses, set):
            raise TypeError("content_licenses must be a set")
        for l in self.content_licenses:
            if not isinstance(l, str):
                raise TypeError("content_licenses must be a set of strings")
        if not isinstance(self.buildrequires, dict):
            raise TypeError("buildrequires must be a dictionary")
        for n, v in self.buildrequires.items():
            if not isinstance(n, str) or not isinstance(v, str):
                raise TypeError("buildrequires keys and values must be strings")
        if not isinstance(self.requires, dict):
            raise TypeError("requires must be a dictionary")
        for n, v in self.requires.items():
            if not isinstance(n, str) or not isinstance(v, str):
                raise TypeError("requires keys and values must be strings")
        if not isinstance(self.community, str):
            raise TypeError("community must be a string")
        if not isinstance(self.documentation, str):
            raise TypeError("documentation must be a string")
        if not isinstance(self.tracker, str):
            raise TypeError("tracker must be a string")
        if self.xmd is not None and not isinstance(self.xmd, dict):
            raise TypeError(
                "xmd must be a dictionary or null, current value: {!r}".format(self.xmd)
            )
        if not isinstance(self.profiles, dict):
            raise TypeError("profiles must be a dictionary")
        for p in self.profiles.keys():
            if not isinstance(p, str):
                raise TypeError("profiles keys must be strings")
            if not isinstance(self.profiles[p], ModuleProfile):
                raise TypeError("profiles values must be instances of ModuleProfile")
            if not isinstance(self.profiles[p].description, str):
                raise TypeError("profile description must be string")
            if not isinstance(self.profiles[p].rpms, set):
                raise TypeError("profile rpms must be sets")
            for ps in self.profiles[p].rpms:
                if not isinstance(ps, str):
                    raise TypeError("profile rpms must be sets of strings")
        if not isinstance(self.components, ModuleComponents) \
           and self.components is not None:
            raise TypeError("components must be an instance of ModuleComponents")
        if self.components:
            if self.components.rpms:
                if not isinstance(self.components.rpms, ModuleRPMs):
                    raise TypeError("rpms must be an instance of ModuleRPMs")
                if not isinstance(self.components.rpms.dependencies, bool):
                    raise TypeError("rpms.dependencies must be a boolean")
                if not isinstance(self.components.rpms.api, set):
                    raise TypeError("rpms.api must be a set")
                for a in self.components.rpms.api:
                    if not isinstance(a, str):
                        raise TypeError("rpms.api must be a set of strings")
                if not isinstance(self.components.rpms.filter, set):
                    raise TypeError("rpms.filter must be a set")
                for a in self.components.rpms.filter:
                    if not isinstance(a, str):
                        raise TypeError("rpms.filter must be a set of strings")
                if self.components.rpms.packages:
                    if not isinstance(self.components.rpms.packages, dict):
                        raise TypeError("rpms.packages must be a dictionary")
                    for p, e in self.components.rpms.packages.items():
                        if not isinstance(p, str):
                            raise TypeError("rpms.packages keys must be strings")
                        if not isinstance(e, dict):
                            raise TypeError("rpms.packages values must dictionaries")
                        for k, v in e.items():
                            if not isinstance(k, str):
                                raise TypeError("rpms extras keys must be strings")
                            if k == "rationale" and v:
                                if not isinstance(v, str):
                                    raise TypeError("rpms rationale must be a string")
                            if k == "commit" and v:
                                if not isinstance(v, str):
                                    raise TypeError("rpms commit must be a string")
                            if k == "repository" and v:
                                if not isinstance(v, str):
                                    raise TypeError("rpms repository must be a string")
                            if k == "cache" and v:
                                if not isinstance(v, str):
                                    raise TypeError("rpms cache must be a string")
                            if k == "arches" and v:
                                if not isinstance(v, list):
                                    raise TypeError("rpms arches must be a list")
                                for s in v:
                                    if not isinstance(s, str):
                                        raise TypeError("arches must be a list of strings")
                            if k == "multilib" and v:
                                if not isinstance(v, list):
                                    raise TypeError("rpms multilib must be a list")
                                for s in v:
                                    if not isinstance(s, str):
                                        raise TypeError("multilib must be a list of strings")
        if not self.name:
            raise ValueError("name is required")
        if not self.version:
            raise ValueError("version is required")
        if not self.release:
            raise ValueError("release is required")
        if not self.summary:
            raise ValueError("summary is required")
        if not self.description:
            raise ValueError("description is required")
        if not self.module_licenses:
            raise ValueError("at least one module license is required")
        if self.components:
            if self.components.rpms:
                for p, e in self.components.rpms.packages.items():
                    if "rationale" not in e:
                        raise ValueError(p, "has no rationale")
        # TODO: Validate dependency version formats
        return True

    @property
    def mdversion(self):
        """An int property representing the metadata format version used.

        This is automatically set to the highest supported version for
        new objects or set by the loaded document.  This value can be
        changed to one of the supported_mdversions to alter the output
        format.
        """
        return self._mdversion

    @mdversion.setter
    def mdversion(self, i):
        if i not in supported_mdversions:
            raise ValueError("Unsupported metadata version")
        self._mdversion = int(i)

    @property
    def name(self):
        """A string property representing the name of the module."""
        return self._name

    @name.setter
    def name(self, s):
        self._name = str(s)

    @property
    def version(self):
        """A string property representing the version of the module."""
        return self._version

    @version.setter
    def version(self, s):
        self._version = str(s)

    @property
    def release(self):
        """A string property representing the release of the module."""
        return self._release

    @release.setter
    def release(self, s):
        self._release = str(s)

    @property
    def summary(self):
        """A string property representing a short summary of the module."""
        return self._summary

    @summary.setter
    def summary(self, s):
        self._summary = str(s)

    @property
    def description(self):
        """A string property representing a detailed description of the
        module."""
        return self._description

    @description.setter
    def description(self, s):
        self._description = str(s)

    @property
    def module_licenses(self):
        """A set of strings, a property, representing the license terms
        of the module itself."""
        return self._module_licenses

    @module_licenses.setter
    def module_licenses(self, ss):
        if not isinstance(ss, set):
            raise TypeError("module_licenses requires a set")
        self._module_licenses = set([str(x) for x in ss])

    def add_module_license(self, s):
        """Adds a module license to the set.

        :param str s: License name
        """
        self._module_licenses.add(str(s))

    def del_module_license(self, s):
        """Removes the supplied license from the module licenses set.

        :param str s: License name
        """
        self._module_licenses.discard(str(s))

    def clear_module_licenses(self):
        """Clears the module licenses set."""
        self._module_licenses.clear()

    @property
    def content_licenses(self):
        """A set of strings, a property, representing the license terms
        of the module contents."""
        return self._content_licenses

    @content_licenses.setter
    def content_licenses(self, ss):
        if not isinstance(ss, set):
            raise TypeError("content_licenses requires a set")
        self._content_licenses = set([str(x) for x in ss])

    def add_content_license(self, s):
        """Adds a content license to the set.

        :param str s: License name
        """
        self._content_licenses.add(str(s))

    def del_content_license(self, s):
        """Removes the supplied license from the content licenses set.

        :param str s: License name
        """
        self._content_licenses.discard(str(s))

    def clear_content_licenses(self):
        """Clears the content licenses set."""
        self._content_licenses.clear()

    @property
    def requires(self):
        """A dictionary property representing the required dependencies of
        the module.

        Keys are the required module names (strings), values are their
        mininum required versions (also strings).
        """
        return self._requires

    @requires.setter
    def requires(self, d):
        if d and not isinstance(d, dict):
            raise TypeError("Incorrect data type passed for requires")
        if d:
            self._requires = { str(k) : str(v) for k, v in d.items() }
        else:
            self._requires = dict()

    def add_requires(self, n, v):
        """Adds a required module dependency.

        :param str n: Required module name
        :param str v: Required module version
        """
        self._requires[str(n)] = str(v)

    update_requires = add_requires

    def del_requires(self, n):
        """Deletes the dependency on the supplied module.

        :param str n: Required module name
        """
        if str(n) in self._requires:
            del self._requires[str(n)]

    def clear_requires(self):
        """Removes all required runtime dependencies."""
        self._requires = dict()

    @property
    def buildrequires(self):
        """A dictionary property representing the required build dependencies
        of the module.

        Keys are the required module names (strings), values are their
        minimum required versions (also strings).
        """
        return self._buildrequires

    @buildrequires.setter
    def buildrequires(self, d):
        if d and not isinstance(d, dict):
            raise TypeError("Incorrect data type passed for buildrequires")
        self._buildrequires = { str(k) : str(v) for k, v in d.items() }

    def add_buildrequires(self, n, v):
        """Adds a module build dependency.

        :param str n: Required module name
        :param str v: Required module version
        """
        self._buildrequires[str(n)] = str(v)

    update_buildrequires = add_buildrequires

    def del_buildrequires(self, n):
        """Deletes the build dependency on the supplied module.

        :param str n: Required module name
        """
        if str(n) in self._buildrequires:
            del self._buildrequires[str(n)]

    def clear_buildrequires(self):
        """Removes all build dependencies."""
        self._buildrequires = dict()

    @property
    def community(self):
        """A string property representing a link to the upstream community
        for this module."""
        return self._community

    @community.setter
    def community(self, s):
        self._community = str(s)

    @property
    def documentation(self):
        """A string property representing a link to the upstream
        documentation for this module."""
        return self._documentation

    @documentation.setter
    def documentation(self, s):
        self._documentation = str(s)

    @property
    def tracker(self):
        """A string property representing a link to the upstream bug tracker
        for this module."""
        return self._tracker

    @tracker.setter
    def tracker(self, s):
        self._tracker = str(s)

    @property
    def xmd(self):
        """A dictionary property containing user-defined data."""
        return self._xmd

    @xmd.setter
    def xmd(self, d):
        if d and not isinstance(d, dict):
            raise TypeError("Incorrect data supplied for xmd")
        self._xmd = d

    @property
    def profiles(self):
        """A dictionary property representing the module profiles."""
        return self._profiles

    @profiles.setter
    def profiles(self, o):
        if not isinstance(o, dict):
            raise TypeError("Incorrect data types passed for profiles")
        self._profiles = o

    @property
    def components(self):
        """A ModuleComponents instance property representing the components
        defining the module."""
        return self._components

    @components.setter
    def components(self, o):
        if o and not isinstance(o, ModuleComponents):
            raise TypeError("Incorrect data type passed for components")
        self._components = o