Example #1
0
def dump_capital_into_report(
        # путь, где будет сохранён отчёт
        ws_dir,
        # настройки генерации отчёта
        report_options,
        # sde данные, загруженные из .converted_xxx.json файлов
        sde_type_ids,
        sde_bp_materials,
        sde_market_groups,
        sde_icon_ids,
        # esi данные, загруженные с серверов CCP
        corp_assets_data,
        corp_industry_jobs_data,
        corp_blueprints_data,
        eve_market_prices_data):
    product_name = report_options["product"]
    file_name: str = report_options.get('assignment', product_name)
    glf = open('{dir}/{fnm}.html'.format(dir=ws_dir,
                                         fnm=render_html.__camel_to_snake(
                                             file_name, True)),
               "wt+",
               encoding='utf8')
    try:
        render_html.__dump_header(glf, product_name)
        __dump_corp_capital(glf, report_options, sde_type_ids,
                            sde_bp_materials, sde_market_groups, sde_icon_ids,
                            corp_assets_data, corp_industry_jobs_data,
                            corp_blueprints_data, eve_market_prices_data)
        render_html.__dump_footer(glf)
    finally:
        glf.close()
Example #2
0
def dump_shareholders_into_report(ws_dir, shareholders_data):
    corporation_name = shareholders_data["corporation"]["name"]
    glf = open('{dir}/shareholders_{fnm}.html'.format(
        dir=ws_dir, fnm=render_html.__camel_to_snake(corporation_name, True)),
               "wt+",
               encoding='utf8')
    try:
        render_html.__dump_header(
            glf, 'Shareholders of {}'.format(corporation_name))
        __dump_corp_shareholders(glf, shareholders_data)
        render_html.__dump_footer(glf)
    finally:
        glf.close()
Example #3
0
def __dump_corp_accounting_details(glf, __association, __keys,
                                   corporation_name, corporation_id,
                                   __corp_tree, __corp_hangar_names,
                                   sde_type_ids, sde_icon_ids):
    render_html.__dump_any_into_modal_header(
        glf, '<span class="text-primary">{nm}</span> {key}'.format(
            nm=corporation_name, key=__association),
        '{nm}_{key}'.format(nm=corporation_id,
                            key="all" if __keys is None else
                            render_html.__camel_to_snake(__association)),
        "btn-xs", "details&hellip;", "modal-lg")
    __roots = __corp_tree.keys()
    for root in __roots:
        __dump_corp_accounting_nested(glf, root, __corp_tree[str(root)],
                                      __corp_hangar_names, sde_type_ids,
                                      sde_icon_ids,
                                      __keys)  # ["CorpDeliveries"]
    render_html.__dump_any_into_modal_footer(glf)
def dump_market_analyzer_into_report(
        # путь, где будет сохранён отчёт
        ws_dir,
        # sde данные, загруженные из .converted_xxx.json файлов
        sde_icon_ids,
        sde_market_groups,
        sde_inv_names,
        # данные, полученные в результате анализа и перекомпоновки входных списков
        market_regions):
    glf = open('{dir}/{rfn}.html'.format(dir=ws_dir, rfn=q_market_analyzer_settings.g_report_filename), "wt+", encoding='utf8')
    try:
        render_html.__dump_header(glf, "Markets")
        __dump_market_analyzer(
            glf,
            sde_icon_ids,
            sde_market_groups,
            market_regions
        )
        render_html.__dump_footer(glf)
    finally:
        glf.close()

    # market_regions tuple: (region_id, region_name, region_details)
    for region_tuple in market_regions:
        glf = open('{dir}/{rfn}-{fnm}.html'.format(
            dir=ws_dir,
            rfn=q_market_analyzer_settings.g_report_filename,
            fnm=render_html.__camel_to_snake(region_tuple[1], True)), "wt+", encoding='utf8')
        try:
            render_html.__dump_header(glf, "{} Markets".format(region_tuple[1]))
            __dump_region_market_analyzer(
                glf,
                sde_icon_ids,
                sde_market_groups,
                sde_inv_names,
                region_tuple
            )
            render_html.__dump_footer(glf)
        finally:
            glf.close()
Example #5
0
def __dump_corp_accounting_nested_tbl(glf, loc_id, loc_dict,
                                      __corp_hangar_names, sde_type_ids,
                                      sde_icon_ids, filter_flags):
    h3_and_table_printed = False
    tr_divider_skipped = True
    __hangar_colors = [
        "", "fbfbfe", "f0f5f3", "f8fcf4", "fffdeb", "fff4f5", "f0f1f9",
        "f8f6f5", "fcfdfd"
    ]
    if "items" in loc_dict:
        __itms_keys = loc_dict["items"].keys()
        for __loc_id in __itms_keys:
            itm_dict = loc_dict["items"][str(__loc_id)]
            # отбрасываем элементы не по фильтру (например нет списка "delivery")
            __filter_found = filter_flags is None
            if not __filter_found:
                for __filter in filter_flags:
                    if __filter in itm_dict["flags"]:
                        __filter_found = True
                        break
            if not __filter_found:
                continue
            # пишем заголовок таблицы (название системы)
            if not h3_and_table_printed:
                h3_and_table_printed = True
                __loc_name = loc_dict["loc_name"]
                if __loc_name is None:
                    __loc_name = loc_id
                glf.write('<h3>{where}<!--{id}--></h3>\n'.format(
                    where='{} '.format(__loc_name)
                    if not (__loc_name is None) else "",
                    id=loc_id))
                glf.write("""
<div class="table-responsive">
  <table class="table table-condensed">
<thead>
 <tr>
  <th style="width:32px;">#</th>
  <th style="width:32px;"></th>
  <th>Items</th>
  <th style="text-align: right;">Cost, ISK</th>
  <th style="text-align: right;">Volume, m&sup3;</th>
 </tr>
</thead>
<tbody>
""")
            # получаем данные по текущему справочнику
            loc_name = itm_dict["loc_name"]
            foreign = itm_dict["foreign"]
            forbidden = itm_dict[
                "forbidden"] if "forbidden" in itm_dict else False
            type_id = itm_dict["type_id"]
            if loc_name is None:
                loc_name = loc_id
            # добавляем пустую строку для разграничения групп товаров между станциями
            if not tr_divider_skipped:
                glf.write('<tr><td colspan="5"></td></tr>')
            tr_divider_skipped = False
            # добавляем название станции
            __station_type_name = eve_sde_tools.get_item_name_by_type_id(
                sde_type_ids, type_id) if not (type_id is None) else ""
            glf.write(
                '<tr><td colspan="5">'
                '<div class="media">'
                ' <div class="media-left">{img}</div>'
                ' <div class="media-body"><strong>{where}</strong>{what}{foreign}{forbidden}<!--{id}--></div>'
                '</div>'
                '</td></tr>\n'.format(
                    where='{} '.format(loc_name)
                    if not (loc_name is None) else "",
                    id=__loc_id,
                    foreign=
                    '&nbsp;<small><span class="label label-warning">foreign</span></small>'
                    if foreign else "",
                    forbidden=
                    '&nbsp;<small><span class="label label-danger">forbidden</span></small>'
                    if forbidden else "",
                    img='<img class="media-object icn32" src="{src}">'.format(
                        src=render_html.__get_img_src(type_id, 32))
                    if not (type_id is None) else "",
                    what='&nbsp;<small>{}</small> '.format(__station_type_name)
                    if __station_type_name else ""))
            row_id = 1
            __summary_cost = None
            __summary_volume = 0
            __blueprints_reactions_dict = None
            if "flags" in itm_dict:
                __f_keys = itm_dict["flags"].keys()
                for hangar_type_flags in range(2):
                    for __flag in __f_keys:  # "CorpDeliveries"
                        # в начало таблицы размещаем офисы на станке с ангарами, так что в конце таблиц размещено всё остальное (без ангаров)
                        if (hangar_type_flags
                                == 0) and (__flag != "OfficeFolder"):
                            continue
                        elif (hangar_type_flags == 1) and (__flag
                                                           == "OfficeFolder"):
                            continue
                        # отбрасываем элементы не по фильтру (например нет списка "delivery")
                        __filter_found = filter_flags is None
                        if not __filter_found and (0 !=
                                                   filter_flags.count(__flag)):
                            __filter_found = True
                        if not __filter_found:
                            continue
                        # получаем список групп товаров, хранящихся в указанном __flag
                        __flag_dict = itm_dict["flags"][str(__flag)]
                        if str(__flag) == "BlueprintsReactions":
                            __blueprints_reactions_dict = __flag_dict
                            continue
                        # сортируем группы товаров на названию групп
                        __flag_dict_sorted = []
                        __g_h_keys = __flag_dict.keys()
                        for __group_hangar_key in __g_h_keys:
                            __group_dict = __flag_dict[str(__group_hangar_key)]
                            __hangar = 0 if __group_dict[
                                "hangar_num"] is None else int(
                                    __group_dict["hangar_num"])
                            __flag_dict_sorted.append({
                                "key":
                                __group_hangar_key,
                                "nm":
                                '{}_{}'.format(__hangar,
                                               __group_dict["group"]),
                                "hg":
                                __hangar
                            })
                        __flag_dict_sorted.sort(key=lambda s: s["nm"])
                        # подсчёт кол-ва групп товаров, лежащих в ангарах (необходимо для вывода hangar' summary)
                        __hangar_num_qty = [0, 0, 0, 0, 0, 0, 0, 0, 0]
                        for __group_dict_sorted in __flag_dict_sorted:
                            __hangar_num_qty[int(
                                __group_dict_sorted["hg"])] += 1
                        # подготавливаем к выводу номера ангаров (если присутствуют)
                        __prev_hangar_num = None
                        __summary_hangar_groups = None
                        __summary_hangar_cost = None
                        __summary_hangar_volume = None
                        # выводим информацию по содержимому location (группы товаров)
                        for __group_dict_sorted in __flag_dict_sorted:
                            __group_hangar_key = __group_dict_sorted["key"]
                            __group_dict = __flag_dict[str(__group_hangar_key)]
                            __hangar_num = __group_dict[
                                "hangar_num"]  # м.б. None, в то время как __group_dict_sorted["hg"] м.б. 0
                            # вывод номера ангара
                            if (__prev_hangar_num is
                                    None) and not (__hangar_num is None) or (
                                        __prev_hangar_num != __hangar_num):
                                __prev_hangar_num = __hangar_num
                                __summary_hangar_groups = __hangar_num_qty[int(
                                    __hangar_num)]
                                __summary_hangar_cost = 0
                                __summary_hangar_volume = 0
                                __hangar_name = next(
                                    (hn["name"] for hn in __corp_hangar_names
                                     if (hn['division'] == __hangar_num) and (
                                         "name" in hn)), None)
                                glf.write(
                                    '<tr style="font-weight:bold;background-color:#{hngr_clr}">'
                                    ' <td colspan="5">{nm}</td>'
                                    '</tr>\n'.format(
                                        nm="{nm} [{num}]".format(
                                            nm=__hangar_name, num=__hangar_num)
                                        if not (__hangar_name is None) else
                                        "{num} Hangar [{num}]".format(
                                            num=__hangar_num),
                                        hngr_clr=__hangar_colors[__hangar_num])
                                )
                            # создание искусственной вложенности (ангары и прочие категории)
                            if not (__hangar_num is None):
                                glf.write(
                                    '<tr{hngr_clr}>'
                                    ' <td></td>'
                                    ' <th scope="row">{num}</th>'
                                    ' <td>'.format(
                                        hngr_clr=' style="background-color:#{}"'
                                        .format(__hangar_colors[__hangar_num])
                                        if not (__hangar_num is None) else "",
                                        num=row_id))
                            else:
                                glf.write(
                                    '<tr>'
                                    ' <th scope="row">{num}</th>\n'
                                    ' <td colspan="2">'.format(num=row_id))
                            # вывод названий товаров, стоимость и объём (строка таблицы)
                            glf.write(
                                ' {icn}{nm}{tag}</td>'
                                ' <td align="right">{cost:,.1f}</td>'
                                ' <td align="right">{volume:,.1f}</td>'
                                '</tr>'.format(
                                    nm=__group_dict["group"],
                                    icn=
                                    '<img class="icn16" src="{}" style="display:inline;">&nbsp;'
                                    .format(
                                        render_html.__get_icon_src(
                                            __group_dict["icon"],
                                            sde_icon_ids)) if
                                    not (__group_dict["icon"] is None) else "",
                                    cost=__group_dict["cost"],
                                    volume=__group_dict["volume"],
                                    tag='' if not (filter_flags is None) and
                                    (len(filter_flags) == 1) else
                                    ' <small><span class="label label-default">{flag}</span></small>'
                                    .format(flag=render_html.__camel_to_snake(
                                        str(__flag)))))
                            # вывод данных по кораблям, находящимся в этой локации
                            __td_ships = ''
                            if "ships" in __group_dict:
                                __ships = __group_dict["ships"]
                                # сортировка по названиям
                                for ship in __ships:
                                    ship.update({
                                        "nm":
                                        eve_sde_tools.get_item_name_by_type_id(
                                            sde_type_ids, ship["type_id"])
                                    })
                                __ships.sort(key=lambda s: s["nm"])
                                # подготовка в выводу в подвале раздела Ships
                                for ship in __ships:
                                    __ship_type_id = ship["type_id"]
                                    __td_cost = '{cost:,.1f}'.format(
                                        cost=ship["cost"])
                                    __td_volume = '{volume:,.1f}'.format(
                                        volume=ship["volume"] +
                                        ship["volume_nested"])
                                    __tag_nested = ''
                                    if ship["volume_nested"] > 0:
                                        __td_cost += ' <mark>+ {cost:,.1f}</mark>'.format(
                                            cost=ship["cost_nested"])
                                    else:
                                        __tag_nested = ' <span class="label label-primary">hull only</span>'
                                    glf.write(
                                        '<tr style="font-size: x-small;{hngr_clr}">'
                                        ' <td colspan="2"></td>'
                                        ' <td>&nbsp; <img class="icn16" src="{src}" style="display:inline;"> <strong>{q}x</strong> {nm}{tagn}</td>'
                                        ' <td align="right">{cost}</td>'
                                        ' <td align="right">{volume}</td>'
                                        '</tr>\n'.format(
                                            hngr_clr=''
                                            if __hangar_num is None else
                                            'background-color:#{};'.format(
                                                __hangar_colors[__hangar_num]),
                                            src=render_html.__get_img_src(
                                                __ship_type_id, 32),
                                            nm=ship["nm"],
                                            tagn=__tag_nested,
                                            q=ship["quantity"],
                                            cost=__td_cost,
                                            volume=__td_volume))
                            row_id = row_id + 1
                            __summary_cost = __group_dict[
                                "cost"] if __summary_cost is None else __summary_cost + __group_dict[
                                    "cost"]
                            __summary_volume += __group_dict["volume"]
                            # вывод summary-информации по ангару
                            if not (__summary_hangar_cost is None):
                                __summary_hangar_cost += __group_dict["cost"]
                                __summary_hangar_volume += __group_dict[
                                    "volume"]
                            if not (__summary_hangar_groups is None):
                                __summary_hangar_groups -= 1
                                if __summary_hangar_groups == 0:
                                    glf.write(
                                        '<tr style="font-weight:bold;background-color:#{hngr_clr}">'
                                        ' <td></td>'
                                        ' <td colspan="2">Summary&nbsp;(<small>Hangar {hangar}</small>)</td>'
                                        ' <td align="right">{cost:,.1f}</td>'
                                        ' <td align="right">{volume:,.1f}</td>'
                                        '</tr>\n'.format(
                                            hangar=__hangar_num,
                                            hngr_clr=__hangar_colors[
                                                __hangar_num],
                                            cost=__summary_hangar_cost,
                                            volume=__summary_hangar_volume))
            # вывод summary-информации в конце каждой таблицы
            if not (__summary_cost is None):
                # не копируется в модальном окне:__copy2clpbrd = '&nbsp;<a data-target="#" role="button" data-copy="{cost:.1f}" class="qind-copy-btn"' \
                # не копируется в модальном окне:                '  data-toggle="tooltip"><span class="glyphicon glyphicon-copy"' \
                # не копируется в модальном окне:                '  aria-hidden="true"></span></a>'. \
                # не копируется в модальном окне:                format(cost=__summary_cost)
                glf.write('<tr style="font-weight:bold;">'
                          ' <td colspan="3">Summary{what}</td>'
                          ' <td align="right">{cost:,.1f}</td>'
                          ' <td align="right">{volume:,.1f}</td>'
                          '</tr>'.format(
                              cost=__summary_cost,
                              volume=__summary_volume,
                              what='&nbsp;(<small>{}</small>)'.format(
                                  __station_type_name)
                              if __station_type_name else ""))
            # вывод пропущенных ранее 'Blueprints & Reactions' (в конце каждой таблицы, под summary)
            if not (__blueprints_reactions_dict is None):
                __flag = "BlueprintsReactions"
                __g_keys = __blueprints_reactions_dict.keys()
                # выводим информацию по содержимому location (группы товаров)
                for __group_id in __g_keys:
                    __group_dict = __blueprints_reactions_dict[str(__group_id)]
                    glf.write(
                        '<tr style="color:green;">'
                        ' <td colspan="3">{icn}{nm}{tag}</td>'
                        ' <td align="right">{cost:,.1f}</td>'
                        ' <td align="right">{volume:,.1f}</td>'
                        '</tr>'.format(
                            nm=__group_dict["group"],
                            icn=
                            '<img class="icn16" src="{}" style="display:inline;">&nbsp;'
                            .format(
                                render_html.__get_icon_src(
                                    __group_dict["icon"], sde_icon_ids))
                            if not (__group_dict["icon"] is None) else "",
                            cost=__group_dict["cost"],
                            volume=__group_dict["volume"],
                            tag='' if not (filter_flags is None) and
                            (len(filter_flags) == 1) else
                            ' <small><span class="label label-default">{flag}</span></small>'
                            .format(flag=render_html.__camel_to_snake(
                                str(__flag)))))
    if h3_and_table_printed:
        glf.write("""
</tbody>
 </table>
</div>
""")
Example #6
0
def __dump_corp_cynonetwork(glf, sde_inv_positions, corp_cynonetwork):
    glf.write("""
<style>
.dropdown-submenu {
  position: relative;
}
.dropdown-submenu .dropdown-menu {
  top: 0;
  left: 100%;
  margin-top: -1px;
}
</style>

<nav class="navbar navbar-default">
 <div class="container-fluid">
  <div class="navbar-header">
   <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-navbar-collapse" aria-expanded="false">
    <span class="sr-only">Toggle navigation</span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
   </button>
   <a class="navbar-brand" data-target="#"><span class="glyphicon glyphicon-random" aria-hidden="true"></span></a>
  </div>
   
  <div class="collapse navbar-collapse" id="bs-navbar-collapse">
   <ul class="nav navbar-nav">
    <li class="dropdown">
     <a data-target="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Routes <span class="caret"></span></a>
      <ul class="dropdown-menu">""")
    cynonet_num = 1
    for cn in q_logist_settings.g_cynonetworks:
        cn_route = cn["route"]
        from_id = cn_route[0]
        from_name = corp_cynonetwork[str(from_id)]["solar_system"]
        to_id = cn_route[-1]
        to_name = corp_cynonetwork[str(to_id)]["solar_system"]
        glf.write(
            '\n       '
            '<li><a id="btnCynoNetSel" cynonet="{cnn}" data-target="#" role="button"><span '
            'class="glyphicon glyphicon-star img-cyno-net" cynonet="{cnn}" aria-hidden="true"></span> '
            '{f} &rarr; {t}</a></li>'
            .  # предполагается: <li><a>JK-Q77 &rarr; Raravath</a></li>
            format(f=from_name, t=to_name, cnn=cynonet_num))
        cynonet_num = cynonet_num + 1
    glf.write("""
       <li role="separator" class="divider"></li>
       <li><a id="btnCynoNetSel" cynonet="0" data-target="#" role="button"><span 
              class="glyphicon glyphicon-star img-cyno-net" cynonet="0" aria-hidden="true"></span> All routes</a></li>
     </ul>
    </li>
    
    <li class="dropdown">
     <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Jump Options <span class="caret"></span></a>
      <ul class="dropdown-menu">

       <li class="dropdown-submenu">
         <a class="options-submenu" data-target="#" role="button">Ship <mark id="lbJumpShip"></mark><span class="caret"></span></a>
         <ul class="dropdown-menu">
           <li class="dropdown-header">[Jump Freighters]</li>
           <li><a id="btnJumpShip" ship="Anshar" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpShipAnshar"></span> Anshar</a></li>
           <li><a id="btnJumpShip" ship="Ark" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpShipArk"></span> Ark</a></li>
           <li><a id="btnJumpShip" ship="Nomad" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpShipNomad"></span> Nomad</a></li>
           <li><a id="btnJumpShip" ship="Rhea" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpShipRhea"></span> Rhea</a></li>
           <li role="separator" class="divider"></li>
           <li><a id="btnJumpAnyShip" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpAnyShip"></span> Any Ship</a></li>
         </ul>
       </li>

       <li class="dropdown-submenu">
         <a class="options-submenu" data-target="#" role="button">Jump Drive Calibration <mark id="lbJumpCalibration"></mark><span class="caret"></span></a>
         <ul class="dropdown-menu">
           <li><a id="btnJumpCalibration" skill="1" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpCalibration1"></span> 1</a></li>
           <li><a id="btnJumpCalibration" skill="2" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpCalibration2"></span> 2</a></li>
           <li><a id="btnJumpCalibration" skill="3" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpCalibration3"></span> 3</a></li>
           <li><a id="btnJumpCalibration" skill="4" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpCalibration4"></span> 4</a></li>
           <li><a id="btnJumpCalibration" skill="5" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpCalibration5"></span> 5</a></li>
         </ul>
       </li>

       <li class="dropdown-submenu">
         <a class="options-submenu" data-target="#" role="button">Jump Fuel Conservation <mark id="lbJumpConservation"></mark><span class="caret"></span></a>
         <ul class="dropdown-menu">
           <li><a id="btnJumpConservation" skill="1" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpConservation1"></span> 1</a></li>
           <li><a id="btnJumpConservation" skill="2" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpConservation2"></span> 2</a></li>
           <li><a id="btnJumpConservation" skill="3" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpConservation3"></span> 3</a></li>
           <li><a id="btnJumpConservation" skill="4" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpConservation4"></span> 4</a></li>
           <li><a id="btnJumpConservation" skill="5" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpConservation5"></span> 5</a></li>
         </ul>
       </li>

       <li class="dropdown-submenu">
         <a class="options-submenu" data-target="#" role="button">Jump Freighter <mark id="lbJumpFreighter"></mark><span class="caret"></span></a>
         <ul class="dropdown-menu">
           <li><a id="btnJumpFreighter" skill="1" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpFreighter1"></span> 1</a></li>
           <li><a id="btnJumpFreighter" skill="2" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpFreighter2"></span> 2</a></li>
           <li><a id="btnJumpFreighter" skill="3" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpFreighter3"></span> 3</a></li>
           <li><a id="btnJumpFreighter" skill="4" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpFreighter4"></span> 4</a></li>
           <li><a id="btnJumpFreighter" skill="5" class="option" data-target="#" role="button"><span class="glyphicon glyphicon-star" aria-hidden="true" id="imgJumpFreighter5"></span> 5</a></li>
         </ul>
       </li>

       <li role="separator" class="divider"></li>
       <li><a id="btnResetOptions" data-target="#" role="button">Reset options</a></li>
      </ul>
    </li>
    
    <li class="disabled"><a data-target="#" role="button">Problems</a></li>
   </ul>
   <form class="navbar-form navbar-right">
    <div class="form-group">
     <input type="text" class="form-control" placeholder="Solar System" disabled>
    </div>
    <button type="button" class="btn btn-default disabled">Search</button>
   </form>
  </div>
 </div>
</nav>
<div class="container-fluid">""")

    cynonetwork_num = 0
    cynonetwork_distances = []
    for cn in q_logist_settings.g_cynonetworks:
        cynonetwork_num = cynonetwork_num + 1
        cn_route = cn["route"]
        from_id = cn_route[0]
        from_name = corp_cynonetwork[str(from_id)]["solar_system"]
        to_id = cn_route[-1]
        to_name = corp_cynonetwork[str(to_id)]["solar_system"]
        url = ""
        human_readable = ""
        route_signalling_level = 0
        for location_id in cn_route:
            route_place = corp_cynonetwork[str(location_id)]
            system_name = route_place["solar_system"]
            if not url:
                url = system_name
                # from_name = system_name
                human_readable = system_name
            else:
                url = url + ":" + system_name
                # to_name = system_name
                human_readable = human_readable + " &rarr; " + system_name
            route_signalling_level = max(route_signalling_level,
                                         route_place["signalling_level"])
        route_signalling_type = __get_route_signalling_type(
            route_signalling_level)
        # ---
        glf.write(
            '<div class="panel panel-{signal} pn-cyno-net" cynonet="{cnn}">\n'
            ' <div class="panel-heading"><h3 class="panel-title">{signal_sign}{nm}</h3></div>\n'
            '  <div class="panel-body">\n'
            '   <p>Checkout Dotlan link for graphical route building: <a class="lnk-dtln" cynonet="{cnn}" routes="{rs}" href="https://evemaps.dotlan.net/jump/Rhea,544/{url}" class="panel-link">https://evemaps.dotlan.net/jump/Rhea,544/{url}</a></p>\n'
            '   <div class="progress">\n'.
            format(  #nm='{} &rarr; {}'.format(from_name, to_name),
                cnn=cynonetwork_num,
                rs=len(cn_route),
                nm=human_readable,
                url=url,
                signal=route_signalling_type,
                signal_sign=
                '<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> '
                if route_signalling_type == "danger" else ""))
        progress_segments = len(cn_route)
        progress_width = int(100 / progress_segments)
        progress_times = 1
        for location_id in cn_route:
            route_place = corp_cynonetwork[str(location_id)]
            system_name = route_place["solar_system"]
            if progress_times == progress_segments:
                progress_width = progress_width + 100 - progress_width * progress_segments
            if not ("error" in route_place):
                glf.write(
                    '    <div id="prgrCynoRoute{cnn}_{pt}" class="progress-bar progress-bar-{signal} signal" role="progressbar" style="width:{width}%">{nm}</div>\n'
                    .format(width=progress_width,
                            nm=system_name,
                            signal=__get_route_signalling_type(
                                route_place["signalling_level"]),
                            cnn=cynonetwork_num,
                            pt=progress_times))
            else:
                glf.write(
                    '    <div class="progress-bar signal" role="progressbar" style="width:{width}%; background-color:#888;">{nm}</div>\n'
                    .format(width=progress_width, nm=system_name))
            progress_times = progress_times + 1
        glf.write("""   </div>
   <table class="table qind-tbl-cynonet">
    <thead>
     <tr>
      <th>#</th>
      <th>Solar System</th>
""")
        glf.write(
            '<th><img src="{src648}" width="32px" height="32px" alt="Badger"/></th>\n'
            '<th><img src="{src32880}" width="32px" height="32px" alt="Venture"/><img\n'
            ' src="{src1317}" width="32px" height="32px" alt="Expanded Cargohold I"/><img\n'
            ' src="{src31117}" width="32px" height="32px" alt="Small Cargohold Optimization I"/></th>\n'
            '<th><img src="{src52694}" width="32px" height="32px" alt="Industrial Cynosural Field Generator"/></th>\n'
            '<th><img src="{src16273}" width="32px" height="32px" alt="Liquid Ozone"/></th>\n'
            .format(
                src648=render_html.__get_img_src(648, 32),
                src32880=render_html.__get_img_src(32880, 32),
                src1317=render_html.__get_img_src(1317, 32),
                src31117=render_html.__get_img_src(31117, 32),
                src52694=
                "https://imageserver.eveonline.com/Type/52694_32.png",  # there are no in IEC: render_html.__get_img_src(52694,32),
                src16273=render_html.__get_img_src(16273, 32)))
        glf.write(
            """<th class="nitrogen">Nitrogen</th><th class="hydrogen">Hydrogen</th><th class="oxygen">Oxygen</th><th class="helium">Helium</th>
     </tr>
    </thead>
    <tbody>
""")
        # --- расчёт дистанции прыжка
        prev_system_id = None
        row_num = 1
        lightyear_distances = []
        for location_id in cn_route:
            route_place = corp_cynonetwork[str(location_id)]
            system_id = route_place["system_id"]
            if row_num > 1:
                pos1 = sde_inv_positions[str(
                    system_id)] if not (system_id is None) and (
                        str(system_id) in sde_inv_positions) else None
                pos2 = sde_inv_positions[str(
                    prev_system_id)] if not (prev_system_id is None) and (
                        str(prev_system_id) in sde_inv_positions) else None
                if not (pos1 is None) and not (pos2 is None):
                    # https://en.wikipedia.org/wiki/Euclidean_distance
                    distance = math.sqrt((pos1["x"] - pos2["x"])**2 +
                                         (pos1["y"] - pos2["y"])**2 +
                                         (pos1["z"] - pos2["z"])**2)
                    # https://github.com/nikdoof/cynomap
                    # ...Distance calculation is based on CCP's lightyear being 9460000000000000 meters, instead of
                    # the actual value of 9460730472580800 meters...
                    distance = distance / 9460000000000000
                    lightyear_distances.append(distance)  # lightyears
                    # https://wiki.eveuniversity.org/Jump_drives#Jumpdrive_Isotope_Usage_Formula
                    # ... moved to javascript logic ...
                else:
                    lightyear_distances.append(None)
            prev_system_id = system_id
            row_num = row_num + 1
        cynonetwork_distances.append(lightyear_distances)
        # --- построение таблицы по маршруту циносети
        row_num = 1
        for location_id in cn_route:
            route_place = corp_cynonetwork[str(location_id)]
            # ---
            tickers_html = ""
            if "found_tickers" in route_place:
                for ft in route_place["found_tickers"]:
                    tickers_html += '<small>&nbsp;<span class="label label-default">{t}</span></small>'.format(
                        t=render_html.__camel_to_snake(ft, False), )
            # ---
            system_name = route_place["solar_system"]
            lightyears = lightyear_distances[
                row_num - 1] if row_num < len(cn_route) else None
            if not ("error"
                    in route_place) or (route_place["error"] != "no data"):
                badger_num = route_place["badger"]
                venture_num = route_place["venture"]
                liquid_ozone_num = route_place["liquid_ozone"]
                indus_cyno_gen_num = route_place["indus_cyno_gen"]
                exp_cargohold_num = route_place["exp_cargohold"]
                cargohold_rigs_num = route_place["cargohold_rigs"]
                nitrogen_isotope_num = route_place["nitrogen_isotope"]
                hydrogen_isotope_num = route_place["hydrogen_isotope"]
                oxygen_isotope_num = route_place["oxygen_isotope"]
                helium_isotope_num = route_place["helium_isotope"]
                badger_jumps_num = min(badger_num, indus_cyno_gen_num,
                                       int(liquid_ozone_num / 950))
                venture_jumps_num = min(venture_num, indus_cyno_gen_num,
                                        int(liquid_ozone_num / 200),
                                        exp_cargohold_num,
                                        int(cargohold_rigs_num / 3))
                glf.write(
                    '<tr id="rowCynoRoute{cnn}_{num}" system="{nm}">\n'
                    ' <th scope="row">{num}</th><td>{nm}{ct}</td>\n'
                    ' <td><abbr title="{bjumps} Badger cynos">{b:,d}</abbr></td>\n'
                    ' <td><abbr title="{vjumps} Venture cynos">{v:,d}</abbr> / {ch:,d} / {chr:,d}</td>\n'
                    ' <td>{icg:,d}</td><td>{lo:,d}</td>\n'
                    ' <td class="nitrogen" id="niCynoRoute{cnn}_{num}">{ni:,d}</td>\n'
                    ' <td class="hydrogen" id="hyCynoRoute{cnn}_{num}">{hy:,d}</td>\n'
                    ' <td class="oxygen" id="oxCynoRoute{cnn}_{num}">{ox:,d}</td>\n'
                    ' <td class="helium" id="heCynoRoute{cnn}_{num}">{he:,d}</td>\n'
                    '</tr>'.format(num=row_num,
                                   cnn=cynonetwork_num,
                                   nm=system_name,
                                   ct=tickers_html,
                                   bjumps=badger_jumps_num,
                                   vjumps=venture_jumps_num,
                                   b=badger_num,
                                   v=venture_num,
                                   lo=liquid_ozone_num,
                                   icg=indus_cyno_gen_num,
                                   ch=exp_cargohold_num,
                                   chr=cargohold_rigs_num,
                                   ni=nitrogen_isotope_num,
                                   hy=hydrogen_isotope_num,
                                   ox=oxygen_isotope_num,
                                   he=helium_isotope_num))
            else:
                glf.write(
                    '<tr id="rowCynoRoute{cnn}_{num}" system="{nm}">\n'
                    ' <th scope="row">{num}</th><td>{nm}</td>\n'
                    ' <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>\n'
                    '</tr>'.format(num=row_num,
                                   cnn=cynonetwork_num,
                                   nm=system_name))
            if row_num != len(cn_route):
                glf.write(
                    '<tr class="active">\n'
                    ' <th></th><td></td>\n'
                    ' <td colspan="4">{ly}</td><td colspan="4"{ly_val} cynonet="{cnn}" route="{rt}"></td>\n'
                    '</tr>'.format(
                        ly_val=' lightyears="{:0.15f}"'.format(lightyears)
                        if not (lightyears is None) else "",
                        cnn=cynonetwork_num,
                        rt=row_num,
                        ly='Distance: <strong>{:0.3f} ly</strong>'.format(
                            lightyears) if not (lightyears is None) else ""))
            row_num = row_num + 1
        glf.write("""
    </tbody>
    <tfoot>
     <tr>
      <td colspan="2"></td>
      <td colspan="5">
""")
        # добавляем диалоговое окно, в котором будет видно что именно не хватает на маршруте, и в каком количестве?
        render_html.__dump_any_into_modal_header(
            glf, 'What'
            's missing on the route <span class="text-primary">{f} &rarr; {t}</span>?'
            .format(f=from_name,
                    t=to_name), 'NotEnough{cnn}'.format(cnn=cynonetwork_num),
            "btn-sm", "Not enough&hellip;")
        # формируем содержимое модального диалога
        glf.write("""
<div class="table-responsive">
 <table class="table table-condensed table-hover">
<thead>
 <tr>
  <th>#</th>
  <th>Solar System</th>
  <th><img src="https://imageserver.eveonline.com/Type/648_32.png" width="32px" height="32px" alt="Badger"/></th>
  <th><img src="https://imageserver.eveonline.com/Type/32880_32.png" width="32px" height="32px" alt="Venture"/><img
           src="https://imageserver.eveonline.com/Type/1317_32.png" width="32px" height="32px" alt="Expanded Cargohold I"/><img
           src="https://imageserver.eveonline.com/Type/31117_32.png" width="32px" height="32px" alt="Small Cargohold Optimization I"/></th>
  <th><img src="https://imageserver.eveonline.com/Type/52694_32.png" width="32px" height="32px" alt="Industrial Cynosural Field Generator"/></th>
  <th><img src="https://imageserver.eveonline.com/Type/16273_32.png" width="32px" height="32px" alt="Liquid Ozone"/></th>
  <th class="nitrogen">Nitrogen</th><th class="hydrogen">Hydrogen</th><th class="oxygen">Oxygen</th><th class="helium">Helium</th>
 </tr>
</thead>
<tbody>
""")
        # выводим сводную информацию
        row_num = 1
        badger_num_ne_summary = 0
        venture_num_ne_summary = 0
        exp_cargohold_num_ne_summary = 0
        cargohold_rigs_num_ne_summary = 0
        indus_cyno_gen_num_ne_summary = 0
        liquid_ozone_num_ne_summary = 0
        for location_id in cn_route:
            route_place = corp_cynonetwork[str(location_id)]
            system_name = route_place["solar_system"]
            if not ("error"
                    in route_place) or (route_place["error"] != "no data"):
                badger_num = route_place["badger"]
                venture_num = route_place["venture"]
                liquid_ozone_num = route_place["liquid_ozone"]
                indus_cyno_gen_num = route_place["indus_cyno_gen"]
                exp_cargohold_num = route_place["exp_cargohold"]
                cargohold_rigs_num = route_place["cargohold_rigs"]
                #---
                badger_num_ne = 11 - badger_num if badger_num <= 11 else 0
                venture_num_ne = 11 - venture_num if venture_num <= 11 else 0
                exp_cargohold_num_ne = 11 - exp_cargohold_num if exp_cargohold_num <= 11 else 0
                cargohold_rigs_num_ne = 33 - cargohold_rigs_num if cargohold_rigs_num <= 33 else 0
                indus_cyno_gen_num_ne = 11 - indus_cyno_gen_num if indus_cyno_gen_num <= 11 else 0
                liquid_ozone_num_ne = 950 * 11 - liquid_ozone_num if liquid_ozone_num <= (
                    950 * 11) else 0
                #---
                badger_num_ne_summary += badger_num_ne
                venture_num_ne_summary += venture_num_ne
                exp_cargohold_num_ne_summary += exp_cargohold_num_ne
                cargohold_rigs_num_ne_summary += cargohold_rigs_num_ne
                indus_cyno_gen_num_ne_summary += indus_cyno_gen_num_ne
                liquid_ozone_num_ne_summary += liquid_ozone_num_ne
                glf.write('<tr id="rowNotEnough{cnn}_{num}" system="{nm}">\n'
                          ' <th scope="row">{num}</th><td>{nm}</td>\n'
                          ' <td>{bne:,d}</td>\n'
                          ' <td>{vne:,d} / {chne:,d} / {chrne:,d}</td>\n'
                          ' <td>{icgne:,d}</td><td>{lone:,d}</td>\n'
                          ' <td class="nitrogen"></td>\n'
                          ' <td class="hydrogen"></td>\n'
                          ' <td class="oxygen"></td>\n'
                          ' <td class="helium"></td>\n'
                          '</tr>'.format(num=row_num,
                                         cnn=cynonetwork_num,
                                         nm=system_name,
                                         bne=badger_num_ne,
                                         vne=venture_num_ne,
                                         chne=exp_cargohold_num_ne,
                                         chrne=cargohold_rigs_num_ne,
                                         icgne=indus_cyno_gen_num_ne,
                                         lone=liquid_ozone_num_ne))
            else:
                glf.write(
                    '<tr>\n'
                    ' <th scope="row">{num}</th><td>{nm}</td>\n'
                    ' <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>\n'
                    '</tr>'.format(num=row_num, nm=system_name))
            row_num = row_num + 1
        # формируем footer модального диалога (формируем summary по недостающим материалам)
        glf.write("""
</tbody>
<tfoot>
""")
        glf.write(
            '<tr id="rowNotEnoughSummary{cnn}" style="font-weight:bold;">\n'
            ' <td colspan="2" align="right">Summary (not enough):</td>\n'
            ' <td>{b:,d}</td>\n'
            ' <td>{v:,d} / {ch:,d} / {chr:,d}</td>\n'
            ' <td>{icg:,d}</td><td>{lo:,d}</td>\n'
            ' <td class="nitrogen"></td>\n'
            ' <td class="hydrogen"></td>\n'
            ' <td class="oxygen"></td>\n'
            ' <td class="helium"></td>\n'
            '</tr>'.format(cnn=cynonetwork_num,
                           b=badger_num_ne_summary,
                           v=venture_num_ne_summary,
                           ch=exp_cargohold_num_ne_summary,
                           chr=cargohold_rigs_num_ne_summary,
                           icg=indus_cyno_gen_num_ne_summary,
                           lo=liquid_ozone_num_ne_summary))
        glf.write("""
</tfoot>
 </table>
</div>
""")
        # закрываем footer модального диалога
        render_html.__dump_any_into_modal_footer(glf)

        glf.write("""
      </td>
      </tr>
    </tfoot>
   </table>
  </div>
 </div>""")
    glf.write("""<hr/>
<h4>Legend</h4>
  <div class="row">
   <div class="col-xs-3">
    <div class="progress">
     <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="95" aria-valuemin="0" aria-valuemax="100" style="width: 95%;"></div>
    </div>
   </div>
   <div class="col-xs-9">10 or more jumps</div>
  </div>

  <div class="row">
   <div class="col-xs-3">
    <div class="progress">
     <div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100" style="width: 25%;"></div>
    </div>
   </div>
   <div class="col-xs-9">at least 2 jumps</div>
  </div>

  <div class="row">
   <div class="col-xs-3">
    <div class="progress">
     <div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100" style="width: 10%;"></div>
    </div>
   </div>
   <div class="col-xs-9">there is a chance to stop</div>
  </div>

  <div class="row">
   <div class="col-xs-3">
    <div class="progress">
     <div class="progress-bar" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 50%; background-color:#888;"></div>
    </div>
   </div>
   <div class="col-xs-9">there are temporary problems with ESI (out of sync assets movements)</div>
  </div>
</div>
<script>
  // Cynonetwork contents (data)
  var contentsCynoNetwork = [
""")
    # выгрузка данных для работы с ними с пом. java-script-а (повторный прогон с накопленными данными)
    cynonetwork_num = 1
    for cn in q_logist_settings.g_cynonetworks:
        glf.write('    [{cnn}, [\n'.format(cnn=cynonetwork_num))
        cn_route = cn["route"]
        route_num = 1
        for location_id in cn_route:
            last_route = route_num == len(cn_route)
            route_place = corp_cynonetwork[str(location_id)]
            system_name = route_place["solar_system"]
            lightyears = 0 if last_route else cynonetwork_distances[
                cynonetwork_num - 1][route_num - 1]
            if not lightyears:
                lightyears = 0
            if not ("error"
                    in route_place) or (route_place["error"] != "no data"):
                nitrogen_isotope_num = route_place["nitrogen_isotope"]
                hydrogen_isotope_num = route_place["hydrogen_isotope"]
                oxygen_isotope_num = route_place["oxygen_isotope"]
                helium_isotope_num = route_place["helium_isotope"]
            else:
                nitrogen_isotope_num = 0
                hydrogen_isotope_num = 0
                oxygen_isotope_num = 0
                helium_isotope_num = 0
            if not ("error" in route_place):
                glf.write(
                    "      [{rn},'{nm}','{signal}',{ly},{ni},{hy},{ox},{he}]{comma}\n"
                    .format(comma=',' if not last_route else ']',
                            rn=route_num,
                            nm=system_name,
                            signal=__get_route_signalling_type(
                                route_place["signalling_level"]),
                            ni=nitrogen_isotope_num,
                            hy=hydrogen_isotope_num,
                            ox=oxygen_isotope_num,
                            he=helium_isotope_num,
                            ly=lightyears))
            else:
                glf.write(
                    "      [{rn},'{nm}','{signal}',{ly},0,0,0,0]{comma}\n".
                    format(comma=',' if not last_route else ']',
                           rn=route_num,
                           nm=system_name,
                           signal=__get_route_signalling_type(3),
                           ly=lightyears))
            route_num = route_num + 1
        glf.write('    ]{comma}\n'.format(
            comma=',' if
            cynonetwork_num != len(q_logist_settings.g_cynonetworks) else ''))
        cynonetwork_num = cynonetwork_num + 1
    # крупный листинг с java-программой (без форматирования)
    glf.write("""  ];
  // Jump Options storage (prepare)
  ls = window.localStorage;
  var knownShips = ['Anshar', 'Ark', 'Nomad', 'Rhea'];
  var usedIsotopeTags = ['th', 'td', 'span'];

  // Tools & Utils
  function numLikeEve(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  }
  // Jump Options storage (init)
  function resetOptionsMenuToDefault() {
    if (!ls.getItem('CynoNetNum')) {
      ls.setItem('CynoNetNum', 0);
    }
    if (!ls.getItem('Ship')) {
      ls.setItem('Ship', 'Rhea');
    }
    if (!ls.getItem('Jump Drive Calibration')) {
      ls.setItem('Jump Drive Calibration', 5);
    }
    if (!ls.getItem('Jump Drive Conservation')) {
      ls.setItem('Jump Drive Conservation', 4);
    }
    if (!ls.getItem('Jump Freighter')) {
      ls.setItem('Jump Freighter', 4);
    }
  }
  // Jump Options storage (rebuild menu components)
  function rebuildOptionsMenu() {
    var cynonet = ls.getItem('CynoNetNum');
    $('span.img-cyno-net').each(function() {
      cn = $(this).attr('cynonet');
      if (cn == cynonet)
        $(this).removeClass('hidden');
      else
        $(this).addClass('hidden');
    });
    var ship = ls.getItem('Ship');
    if (!ship) {
      for (var s of knownShips) {
        $('#imgJumpShip'+s).addClass('hidden');
      }
      $('#imgJumpAnyShip').removeClass('hidden');
      $('#lbJumpShip').addClass('hidden');
    }
    else {
      for (var s of knownShips) {
        if (ship == s)
          $('#imgJumpShip'+s).removeClass('hidden');
        else
          $('#imgJumpShip'+s).addClass('hidden');
      }
      $('#imgJumpAnyShip').addClass('hidden');
      $('#lbJumpShip').html(ship);
      $('#lbJumpShip').removeClass('hidden');
    }
    var skill = ls.getItem('Jump Drive Calibration');
    $('#lbJumpCalibration').html(skill);
    for (var i = 1; i <= 5; i++) {
        if (skill == i)
          $('#imgJumpCalibration'+i.toString()).removeClass('hidden');
        else
          $('#imgJumpCalibration'+i.toString()).addClass('hidden');
    }
    skill = ls.getItem('Jump Drive Conservation');
    $('#lbJumpConservation').html(skill);
    for (var i = 1; i <= 5; i++) {
        if (skill == i)
          $('#imgJumpConservation'+i.toString()).removeClass('hidden');
        else
          $('#imgJumpConservation'+i.toString()).addClass('hidden');
    }
    skill = ls.getItem('Jump Freighter');
    $('#lbJumpFreighter').html(skill);
    for (var i = 1; i <= 5; i++) {
        if (skill == i)
          $('#imgJumpFreighter'+i.toString()).removeClass('hidden');
        else
          $('#imgJumpFreighter'+i.toString()).addClass('hidden');
    }
  }
  // Jump Options storage (rebuild panel links)
  function rebuildPanelLinks() {
    var ship = ls.getItem('Ship');
    if (!ship)
        ship = 'Rhea';
    var calibration_skill = parseInt(ls.getItem('Jump Drive Calibration'));
    var conservation_skill = parseInt(ls.getItem('Jump Drive Conservation'));
    var freighter_skill = parseInt(ls.getItem('Jump Freighter'));
    $('a.lnk-dtln').each(function() {
      cynonet = $(this).attr('cynonet');
      routes = $(this).attr('routes');
      var uri = 'https://evemaps.dotlan.net/jump/'+ship+','+calibration_skill+conservation_skill+freighter_skill+'/';
      for (var i = 1; i <= routes; i++) {
        if (i > 1) uri = uri + ':';
        uri = uri + $('#rowCynoRoute'+cynonet+'_'+i.toString()).attr('system');
      }
      $(this).attr('href', uri);
      $(this).html(uri);
    });
  }
  // Isotope Quantity Calculator
  function calcIsotope(lightyears, fuel_consumption, conservation_skill, freighter_skill) {
    return parseInt(lightyears * fuel_consumption * (1 - 0.1 * conservation_skill) * (1 - 0.1 * freighter_skill), 10);
  }
  // Jump Options storage (rebuild progress bar signals)
  function rebuildProgressBarSignals() {
    var ship = ls.getItem('Ship');
    //TODO:var calibration_skill = parseInt(ls.getItem('Jump Drive Calibration'));
    var conservation_skill = parseInt(ls.getItem('Jump Drive Conservation'));
    var freighter_skill = parseInt(ls.getItem('Jump Freighter'));
    for (var cn of contentsCynoNetwork) {
      cn_num = cn[0];
      for (var rt of cn[1]) {
        ly = rt[3];
        if (!ly) continue;
        signal = rt[2];
        if (signal == 'danger') continue;
        rt_num = rt[0];
        var nitrogen_used = calcIsotope(ly, 10000, conservation_skill, freighter_skill);
        var hydrogen_used = calcIsotope(ly, 8200, conservation_skill, freighter_skill);
        var oxygen_used = calcIsotope(ly, 9400, conservation_skill, freighter_skill);
        var helium_used = calcIsotope(ly, 8800, conservation_skill, freighter_skill);
        var nitrogen_times = parseInt(rt[4] / nitrogen_used, 10);
        var hydrogen_times = parseInt(rt[5] / hydrogen_used, 10);
        var oxygen_times = parseInt(rt[6] / oxygen_used, 10);
        var helium_times = parseInt(rt[7] / helium_used, 10);
        prgr = $('#prgrCynoRoute'+cn_num.toString()+'_'+rt_num.toString());
        if (prgr) {
          var times = 0;
          if (!ship) times = Math.min(nitrogen_times, hydrogen_times, oxygen_times, helium_times);
          else if (ship == 'Anshar') times = oxygen_times; // Gallente : Oxygen isotopes
          else if (ship == 'Ark') times = helium_times; // Amarr : Helium isotopes
          else if (ship == 'Nomad') times = hydrogen_times; // Minmatar : Hydrogen isotopes
          else if (ship == 'Rhea') times = nitrogen_times; // Caldari : Nitrogen isotopes
          if (signal == 'warning') {
            if (times >= 10) times = 2;
          }
          if (times < 2) {
            prgr.addClass('progress-bar-danger');
            prgr.removeClass('progress-bar-success');
            prgr.removeClass('progress-bar-warning');
          }
          else if (times >= 10) {
            prgr.addClass('progress-bar-success');
            prgr.removeClass('progress-bar-danger');
            prgr.removeClass('progress-bar-warning');
          }
          else {
            prgr.addClass('progress-bar-warning');
            prgr.removeClass('progress-bar-danger');
            prgr.removeClass('progress-bar-success');
          }
        }
      }
    }
  }
  // Jump Options storage (rebuild body components)
  function rebuildBody() {
    var ship = ls.getItem('Ship');
    //TODO:var calibration_skill = parseInt(ls.getItem('Jump Drive Calibration'));
    var conservation_skill = parseInt(ls.getItem('Jump Drive Conservation'));
    var freighter_skill = parseInt(ls.getItem('Jump Freighter'));
    $('table.qind-tbl-cynonet').each(function() {
      var table = $(this);
      var tbody = table.find('tbody');
      var not_enough_summary = [0,0,0,0];
      var not_enough_cn_num = null;
      tbody.find('tr.active td').each(function() {
        ly = $(this).attr('lightyears');
        if (ly) {
          ly = parseFloat(ly);
          var nitrogen_used = calcIsotope(ly, 10000, conservation_skill, freighter_skill);
          var hydrogen_used = calcIsotope(ly, 8200, conservation_skill, freighter_skill);
          var oxygen_used = calcIsotope(ly, 9400, conservation_skill, freighter_skill);
          var helium_used = calcIsotope(ly, 8800, conservation_skill, freighter_skill);
          not_enough_cn_num = cn_num = $(this).attr('cynonet');
          rt_num = $(this).attr('route');
          times = null
          contents = null
          for (var rt of contentsCynoNetwork[cn_num-1][1]) {
            if (rt[0] == rt_num) {
              contents = [rt[4],rt[5],rt[6],rt[7]]
              times = [parseInt(rt[4]/nitrogen_used), parseInt(rt[5]/hydrogen_used),
                       parseInt(rt[6]/oxygen_used), parseInt(rt[7]/helium_used)];
              break;
            }
          }
          $(this).html('Isotopes needed: <span class="nitrogen"><strong>' + nitrogen_used + '</strong> Ni</span> ' +
                        '<span class="hydrogen"><strong>' + hydrogen_used + '</strong> Hy</span> ' +
                        '<span class="oxygen"><strong>' + oxygen_used + '</strong> Ox</span> ' +
                        '<span class="helium"><strong>' + helium_used + '</strong> He</span>');
          if (times && contents) {
            if (contents[0]) {
              $('#niCynoRoute'+cn_num+'_'+rt_num).html('<abbr title="'+times[0]+' Rhea jumps">'+numLikeEve(contents[0])+'</abbr>');
              var not_enough = (contents[0] >= (nitrogen_used * 11)) ? 0 : ((nitrogen_used * 11) - contents[0]);
              $('#rowNotEnough'+cn_num+'_'+rt_num).find('td').eq(5).html(numLikeEve(not_enough));
              not_enough_summary[0] += not_enough;
            }
            else {
              $('#rowNotEnough'+cn_num+'_'+rt_num).find('td').eq(5).html(numLikeEve(nitrogen_used * 11));
              not_enough_summary[0] += nitrogen_used * 11;
            }
            if (contents[1]) {
              $('#hyCynoRoute'+cn_num+'_'+rt_num).html('<abbr title="'+times[1]+' Nomad jumps">'+numLikeEve(contents[1])+'</abbr>');
              var not_enough = (contents[1] >= (hydrogen_used * 11)) ? 0 : ((hydrogen_used * 11) - contents[1]);
              $('#rowNotEnough'+cn_num+'_'+rt_num).find('td').eq(6).html(numLikeEve(not_enough));
              not_enough_summary[1] += not_enough;
            }
            else {
              $('#rowNotEnough'+cn_num+'_'+rt_num).find('td').eq(6).html(numLikeEve(hydrogen_used * 11));
              not_enough_summary[1] += hydrogen_used * 11;
            }
            if (contents[2]) {
              $('#oxCynoRoute'+cn_num+'_'+rt_num).html('<abbr title="'+times[2]+' Anshar jumps">'+numLikeEve(contents[2])+'</abbr>');
              var not_enough = (contents[2] >= (oxygen_used * 11)) ? 0 : ((oxygen_used * 11) - contents[2]);
              $('#rowNotEnough'+cn_num+'_'+rt_num).find('td').eq(7).html(numLikeEve(not_enough));
              not_enough_summary[2] += not_enough;
            }
            else {
              $('#rowNotEnough'+cn_num+'_'+rt_num).find('td').eq(7).html(numLikeEve(oxygen_used * 11));
              not_enough_summary[2] += hydrogen_used * 11;
            }
            if (contents[3]) {
              $('#heCynoRoute'+cn_num+'_'+rt_num).html('<abbr title="'+times[3]+' Ark jumps">'+numLikeEve(contents[3])+'</abbr>');
              var not_enough = (contents[3] >= (helium_used * 11)) ? 0 : ((helium_used * 11) - contents[3]);
              $('#rowNotEnough'+cn_num+'_'+rt_num).find('td').eq(8).html(numLikeEve(not_enough));
              not_enough_summary[3] += not_enough;
            }
            else {
              $('#rowNotEnough'+cn_num+'_'+rt_num).find('td').eq(8).html(numLikeEve(helium_used * 11));
              not_enough_summary[3] += helium_used * 11;
            }
          }
        }
      });
      var tr_summary = $('#rowNotEnoughSummary'+not_enough_cn_num.toString());
      for (var i = 0; i < 4; ++i)
        tr_summary.find('td').eq(5+i).html(numLikeEve(not_enough_summary[i]));
    });
    if (!ship) { // show all
      for (var t of usedIsotopeTags) {
        $(t+'.nitrogen').removeClass('hidden');
        $(t+'.hydrogen').removeClass('hidden');
        $(t+'.oxygen').removeClass('hidden');
        $(t+'.helium').removeClass('hidden');
      }
    }
    else if (ship == 'Anshar') { // Gallente : Oxygen isotopes
      for (var t of usedIsotopeTags) {
        $(t+'.nitrogen').addClass('hidden');
        $(t+'.hydrogen').addClass('hidden');
        $(t+'.oxygen').removeClass('hidden');
        $(t+'.helium').addClass('hidden');
      }
    }
    else if (ship == 'Ark') { // Amarr : Helium isotopes
      for (var t of usedIsotopeTags) {
        $(t+'.nitrogen').addClass('hidden');
        $(t+'.hydrogen').addClass('hidden');
        $(t+'.oxygen').addClass('hidden');
        $(t+'.helium').removeClass('hidden');
      }
    }
    else if (ship == 'Nomad') { // Minmatar : Hydrogen isotopes
      for (var t of usedIsotopeTags) {
        $(t+'.nitrogen').addClass('hidden');
        $(t+'.hydrogen').removeClass('hidden');
        $(t+'.oxygen').addClass('hidden');
        $(t+'.helium').addClass('hidden');
      }
    }
    else if (ship == 'Rhea') { // Caldari : Nitrogen isotopes
      for (var t of usedIsotopeTags) {
        $(t+'.nitrogen').removeClass('hidden');
        $(t+'.hydrogen').addClass('hidden');
        $(t+'.oxygen').addClass('hidden');
        $(t+'.helium').addClass('hidden');
      }
    }
    var cynonet = ls.getItem('CynoNetNum');
    $('div.pn-cyno-net').each(function() {
      cn = $(this).attr('cynonet');
      if ((cynonet == 0) || (cynonet == cn.toString()))
        $(this).removeClass('hidden');
      else
        $(this).addClass('hidden');
    })
  }

  // Jump Options menu and submenu setup
  $(document).ready(function(){
    $('.dropdown-submenu a.options-submenu').on("click", function(e){
      $(this).next('ul').toggle();
      e.stopPropagation();
      e.preventDefault();
    });
    $('a#btnCynoNetSel').on('click', function() {
      cynonet = $(this).attr('cynonet');
      ls.setItem('CynoNetNum', cynonet);
      rebuildOptionsMenu();
      rebuildBody();
    });
    $('a#btnJumpShip').on('click', function() {
      ship = $(this).attr('ship');
      ls.setItem('Ship', ship);
      rebuildOptionsMenu();
      rebuildPanelLinks();
      rebuildProgressBarSignals();
      rebuildBody();
    });
    $('#btnJumpAnyShip').on('click', function() {
      ls.removeItem('Ship');
      rebuildOptionsMenu();
      rebuildPanelLinks();
      rebuildProgressBarSignals();
      rebuildBody();
    });
    $('a#btnJumpCalibration').on('click', function() {
      skill = $(this).attr('skill');
      ls.setItem('Jump Drive Calibration', skill);
      rebuildOptionsMenu();
      rebuildPanelLinks();
      rebuildProgressBarSignals();
      rebuildBody();
    });
    $('a#btnJumpConservation').on('click', function() {
      skill = $(this).attr('skill');
      ls.setItem('Jump Drive Conservation', skill);
      rebuildOptionsMenu();
      rebuildPanelLinks();
      rebuildProgressBarSignals();
      rebuildBody();
    });
    $('a#btnJumpFreighter').on('click', function() {
      skill = $(this).attr('skill');
      ls.setItem('Jump Freighter', skill);
      rebuildOptionsMenu();
      rebuildPanelLinks();
      rebuildProgressBarSignals();
      rebuildBody();
    });
    $('#btnResetOptions').on('click', function () {
      ls.clear();
      resetOptionsMenuToDefault();
      rebuildOptionsMenu();
      rebuildPanelLinks();
      rebuildProgressBarSignals();
      rebuildBody();
    });
    // first init
    resetOptionsMenuToDefault();
    rebuildOptionsMenu();
    rebuildPanelLinks();
    rebuildProgressBarSignals();
    rebuildBody();
  });
</script>""")
def __dump_market_analyzer(
        glf,
        # sde данные, загруженные из .converted_xxx.json файлов
        sde_icon_ids,
        sde_market_groups,
        # данные, полученные в результате анализа и перекомпоновки входных списков
        market_regions):
    glf.write("""
<nav class="navbar navbar-default">
 <div class="container-fluid">
  <div class="navbar-header">
   <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-navbar-collapse" aria-expanded="false">
    <span class="sr-only">Toggle navigation</span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
   </button>
   <a class="navbar-brand" data-target="#"><span class="glyphicon glyphicon-tasks" aria-hidden="true"></span></a>
  </div>

  <div class="collapse navbar-collapse" id="bs-navbar-collapse">
   <ul class="nav navbar-nav">
    <li class="dropdown">
     <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Display Options <span class="caret"></span></a>
      <ul class="dropdown-menu">
       <li><a id="btnResetOptions" data-target="#" role="button">Reset options</a></li>
      </ul>
    </li>
   </ul>
   <form class="navbar-form navbar-right">
    <div class="form-group">
     <input type="text" class="form-control" placeholder="Item" disabled>
    </div>
    <button type="button" class="btn btn-default disabled">Search</button>
   </form>
  </div>
 </div>
</nav>

<div class="container-fluid">
<h3>Markets</h3>
<table class="table table-condensed table-hover table-responsive">
<tr><th width="13%">Region</th><th width="7%">Orders</th><th width="40%">Sell Orders</th><th width="40%">Buy Orders</th></tr>
""")

    # market_regions tuple: (region_id, region_name, region_details)
    market_regions.sort(
        key=lambda mr: mr[2]["orders"]["sell"] + mr[2]["orders"]["buy"],
        reverse=True)
    the_forge_region_dict = next(mr[2] for mr in market_regions if mr[0] == 10000002)
    the_forge_region_total_orders = the_forge_region_dict["orders"]["sell"] + the_forge_region_dict["orders"]["buy"]

    for (region_id, region_name, region_details) in market_regions:
        # сортируем маркет регионов по интенсивности маркет-групп
        # popular_market_groups = get_popular_market_groups(
        #     region_details["market"], 't',
        #     q_market_analyzer_settings.g_popular_region_groups,
        #     q_market_analyzer_settings.g_popular_region_groups_captions,
        #     sde_icon_ids, sde_market_groups)
        popular_market_groups_buy = get_popular_market_groups(
            region_details["market"], 'b',
            q_market_analyzer_settings.g_popular_region_groups,
            q_market_analyzer_settings.g_popular_region_groups_captions,
            sde_icon_ids, sde_market_groups)
        popular_market_groups_sell = get_popular_market_groups(
            region_details["market"], 's',
            q_market_analyzer_settings.g_popular_region_groups,
            q_market_analyzer_settings.g_popular_region_groups_captions,
            sde_icon_ids, sde_market_groups)

        # считаем кол-во ордеров
        sell = region_details["orders"]["sell"]
        buy = region_details["orders"]["buy"]
        total = sell + buy

        glf.write(
            "<tr id='rgn{id}'>"
            "<td>{nm}<br><a class='btn btn-default' href='{rfn}-{fnm}.html' role='button'><span class='glyphicon glyphicon-shopping-cart' aria-hidden='true'></span></a></td>"
            "<td>{t} {tp}</td>"
            "<td>{s} {sp}<br><small><small>{sd}</small></small></td>"
            "<td>{b} {bp}<br><small><small>{bd}</small></small></td>"
            "</tr>\n".
            format(
                id=region_id,
                nm=region_name,
                rfn=q_market_analyzer_settings.g_report_filename,
                fnm=render_html.__camel_to_snake(region_name, True),
                t=total,
                tp="" if region_id == 10000002 else get_percent_span(the_forge_region_total_orders, sell+buy, color="text-primary"),
                # td=popular_market_groups,
                s=sell, sp=get_percent_span(total, sell),
                sd=popular_market_groups_sell,
                b=buy, bp=get_percent_span(total, buy),
                bd=popular_market_groups_buy,
            ))

    glf.write("""
</table>
</div> <!--container-fluid-->
""")
def __dump_index(glf):
    glf.write("""
<div class="well center-block" style="max-width: 400px;">
  <a href="accounting.html" class="btn btn-primary btn-lg btn-block" role="button">Accounting</a>
  <a href="blueprints.html" class="btn btn-primary btn-lg btn-block" role="button">Blueprints</a>
  <div class="btn-group btn-block">
    <a href="conveyor.html" class="btn btn-primary btn-lg" role="button" style="width:320px;">Conveyor</a>
    <button type="button" class="btn btn-primary btn-lg dropdown-toggle" style="width:39px; float:right;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <span class="caret"></span>
      <span class="sr-only">Variants</span>
    </button>
    <ul class="dropdown-menu" style="left:201px;">
      <li><a href="conveyor.html">Malpais</a></li>
      <li><a href="conveyor-querious.html">Querious</a></li>
    </ul>
  </div>
  <a href="cynonetwork.html" class="btn btn-primary btn-lg btn-block" role="button">Cyno Network</a>
""")
    if q_market_analyzer_settings.g_regions:
        glf.write("""
  <div class="btn-group btn-block">
""")
        glf.write("<a href='{rfn}.html'".format(rfn=q_market_analyzer_settings.g_report_filename))
        glf.write(""" class="btn btn-primary btn-lg" role="button" style="width:320px;">Markets</a>
    <button type="button" class="btn btn-primary btn-lg dropdown-toggle" style="width:39px; float:right;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <span class="caret"></span>
      <span class="sr-only">Variants</span>
    </button>
    <ul class="dropdown-menu" style="left:201px;">
""")
        for rg in q_market_analyzer_settings.g_regions:
            glf.write("<li><a href='{rfn}-{fnm}.html'>{nm}</a></li>\n".
                      format(rfn=q_market_analyzer_settings.g_report_filename, fnm=render_html.__camel_to_snake(rg, True), nm=rg))
        glf.write("""
    </ul>
  </div>
""")
    glf.write("""
  <div class="btn-group btn-block">
    <a href="industry.html" class="btn btn-primary btn-lg" role="button" style="width:320px;">Industry</a>
    <button type="button" class="btn btn-primary btn-lg dropdown-toggle" style="width:39px; float:right;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <span class="caret"></span>
      <span class="sr-only">Variants</span>
    </button>
    <ul class="dropdown-menu" style="left:201px;">
      <li><a href="qi_industry.php">Archive</a></li>
    </ul>
  </div>
  <div class="btn-group btn-block">
    <a href="workflow.html" class="btn btn-primary btn-lg" role="button" style="width:320px;">Workflow</a>
    <button type="button" class="btn btn-primary btn-lg dropdown-toggle" style="width:39px; float:right;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <span class="caret"></span>
      <span class="sr-only">Variants</span>
    </button>
    <ul class="dropdown-menu" style="left:201px;">
      <li><a href="qi_workflow.php">Settings</a></li>
    </ul>
  </div>
  <div class="btn-group btn-block">
    <a href="regroup.html" class="btn btn-primary btn-lg" role="button" style="width:320px;">Regroup</a>
    <button type="button" class="btn btn-primary btn-lg dropdown-toggle" style="width:39px; float:right;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <span class="caret"></span>
      <span class="sr-only">Variants</span>
    </button>
    <ul class="dropdown-menu" style="left:201px;">
      <li><a href="qi_regroup.php">Settings</a></li>
    </ul>
  </div>
  <div class="btn-group btn-block">
    <a href="shareholders_r_initiative4.html" class="btn btn-primary btn-lg" role="button" style="width:320px;">Shareholders</a>
    <button type="button" class="btn btn-primary btn-lg dropdown-toggle" style="width:39px; float:right;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <span class="caret"></span>
      <span class="sr-only">Variants</span>
    </button>
    <ul class="dropdown-menu" style="left:201px;">
      <li><a href="shareholders_r_industry.html">R Industry</a></li>
      <li><a href="shareholders_r_initiative4.html">R Initiative 4</a></li>
      <li><a href="shareholders_r_initiative5.html">R Initiative 5</a></li>
      <li><a href="shareholders_r_strike.html">R Strike</a></li>
      <li><a href="shareholders_night_trade_team.html">Night Trade Team</a></li>
      <li><a href="shareholders_just_a_trade_corp.html">Just A Trade Corp</a></li>
    </ul>
  </div>
""")
    if q_capital_settings.g_report_options:
        products: typing.List[str] = sorted([p for p in set([ro['product'] for ro in q_capital_settings.g_report_options])])
        for product in products:
            report_options = [ro for ro in q_capital_settings.g_report_options if ro['product'] == product]
            if len(report_options) == 1:
                ro = report_options[0]
                fname: str = ro.get('assignment', ro['product'])
                glf.write('<a href="{fnm}.html" class="btn btn-primary btn-lg btn-block" role="button">{nm}</a>\n'.format(
                    fnm=render_html.__camel_to_snake(fname, True),
                    nm=product
                ))
            else:
                ro_first = report_options[0]
                fname_first: str = ro_first.get('assignment', ro_first['product'])
                glf.write(
                    '<div class="btn-group btn-block">\n'
                    '<a href="{fnm}.html" '
                    'class="btn btn-primary btn-lg" role="button" style="width:320px;">{nm}</a>\n'.
                    format(fnm=render_html.__camel_to_snake(fname_first, True),
                           nm=product,
                ))
                glf.write("""<button type="button" class="btn btn-primary btn-lg dropdown-toggle" style="width:39px; float:right;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
  <span class="caret"></span>
  <span class="sr-only">Variants</span>
</button>
<ul class="dropdown-menu" style="left:201px;">
""")
                for ro in report_options:
                    fname: str = ro.get('assignment', ro['product'])
                    glf.write(
                        "<li><a href='{fnm}.html'>{nm}</a></li>\n".
                        format(fnm=render_html.__camel_to_snake(fname, True),
                               nm=fname,
                    ))
                glf.write("</ul>\n</div>\n")

    glf.write("""
</div>
""")