mirror of
https://github.com/coulisse/spiderweb.git
synced 2024-09-21 07:27:09 +00:00
build script
This commit is contained in:
parent
0a85e94787
commit
c780bc4650
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,4 +3,4 @@
|
||||
config.json
|
||||
__init__
|
||||
__pycache__
|
||||
|
||||
.vscode/
|
||||
|
61
README.md
61
README.md
@ -13,7 +13,7 @@
|
||||
- **Release:** v2.4.1
|
||||
- **Author:** Corrado Gerbaldo - [IU1BOW](https://www.qrz.com/db/IU1BOW)
|
||||
- **Mail:** <corrado.gerbaldo@gmail.com>
|
||||
- **Licensing:** Gpl V3.0 see ["LICENSE"](LICENSE) file.
|
||||
- **Licensing:** Gpl V3.0 see [LICENSE](LICENSE) file.
|
||||
- **Languages:** This application is written in Python 3.11/flask,Javascript and HTML
|
||||
|
||||
___
|
||||
@ -233,12 +233,69 @@ This application is designed for desktop and mobile phone. It is a [PWA](https:/
|
||||
|
||||
### API
|
||||
**Spot list**
|
||||
|
||||
You can retrive last spots calling "**/spotlist**"; For example [www.iu1bow.com/spotlist](https://www.iu1bow.com/spotlist)
|
||||
|
||||
**country of a callsign**
|
||||
**Country of a callsign**
|
||||
|
||||
You cam retrive some informations about a callsign with **callsign**; For example [www.iu1bow.com/callsign?c=IU1BOW](https://www.iu1bow.com/callsign?c=IU1BOW)
|
||||
|
||||
|
||||
### Development
|
||||
**Directory structure**
|
||||
```
|
||||
/ . main application files
|
||||
├── cfg . configuration files (put here your config.json with your setting)
|
||||
├── docs . documentation
|
||||
├── lib . python libs used for the application
|
||||
├── log . application log
|
||||
├── scripts . utility scripts for testing, build etc.
|
||||
├── static . static files css, js, data, html, images etc.
|
||||
│ ├── css .
|
||||
│ │ ├── dev . development css not minifyed/uglifyed
|
||||
│ │ └── rel . release css minifyed/uglifyed (do not change these files)
|
||||
│ ├── data . application data (world.json)
|
||||
│ ├── html .
|
||||
│ │ └── dev . html templates used for build release static html (for offline)
|
||||
│ │ └── rel . release static html (for offline)
|
||||
│ ├── images . static images
|
||||
│ │ └── icons . static icons
|
||||
│ └── js .
|
||||
│ ├── dev . development js not minifyed/uglifyed
|
||||
│ └── rel . release js minifyed/uglifyed (do not change these files)
|
||||
└── templates . html templates used by python flask for serve the web pages
|
||||
```
|
||||
**Application description**
|
||||
|
||||
The main **server** application ```webapp.py``` is in the root folder. In this application there are routing to html dynamic templates and serves also back-end API. This is whapped by ```wsgi.py``` for using with **bjoern** server.
|
||||
|
||||
Static files (css, js...) are in ```static``` directory: here there are subdirectories:
|
||||
- ```dev``` where you can edit and modify sources
|
||||
- ```rel``` here there are release files created with the building process and used in producion
|
||||
|
||||
**Lint**
|
||||
|
||||
For lint javascript I use **ESLint**. You can install with ```npm init @eslint/config```
|
||||
pylint ```pip install pylint```
|
||||
|
||||
**Building process**
|
||||
|
||||
Prerequisites:
|
||||
| **Component** | **Description** | **Install command** |
|
||||
|---------------|----------------------------------------------------|---------------------------------|
|
||||
| npm | a packet manager for javascript | depend on your os. See [official page](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) |
|
||||
| uglify-js | npm component used to minify and uglify javascript | ```npm install uglify-js -g``` |
|
||||
| css-minify | npm component used to minify css | ```npm install css-minify -g``` |
|
||||
| staticjinja | python module used to create static page starting from a html template | ```pip install staticjinja``` |
|
||||
|
||||
|
||||
You can build the software for test (dev), or for production (release) environements.
|
||||
In ```scripts``` directory launch:
|
||||
- ```./build.sh -d``` for dev environment
|
||||
|
||||
- ```./build.sh -r``` for release environment
|
||||
|
||||
|
||||
### Screenshots
|
||||
----------
|
||||
|
||||
|
@ -12,6 +12,7 @@ level=INFO
|
||||
handlers=stream_handler,file_handler
|
||||
|
||||
[handler_stream_handler]
|
||||
level=INFO
|
||||
class=StreamHandler
|
||||
formatter=formatter
|
||||
args=(sys.stderr,)
|
||||
|
@ -1,13 +1,15 @@
|
||||
### Change log
|
||||
Date: 02/01/2023
|
||||
Release v.2.4
|
||||
Date: 07/01/2023
|
||||
Release: v2.4.1
|
||||
- changed dimensions of spots in world dx spost charts
|
||||
- managed empty data in data providers for charts
|
||||
- removed jQuery: migrated to vanilla javascript
|
||||
- for spot refresh get only new spots starting from last rowid
|
||||
- modified building script
|
||||
- moved cty file in data directory
|
||||
___
|
||||
Date: 01/01/2023
|
||||
Release v.2.4
|
||||
Release: v2.4
|
||||
- migration to python 3.11
|
||||
- added descriptions to continents
|
||||
- fixed issue #23: Wrong flag for Cocos (Keeling) Islands
|
||||
|
98
lib/adxo.py
98
lib/adxo.py
@ -1,77 +1,85 @@
|
||||
#***********************************************************************************
|
||||
# ***********************************************************************************
|
||||
# Module used to get Announced DX Operation from NG3K website via .ICS (Calendar)
|
||||
# file, parse it and return a dictionary with these events
|
||||
#***********************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
# ***********************************************************************************
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import requests
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import tempfile
|
||||
|
||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
||||
#format single line
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||
datefmt="%m/%d/%Y %I:%M:%S",
|
||||
)
|
||||
# format single line
|
||||
def format_line(prop):
|
||||
prop_out=dict()
|
||||
prop_out = dict()
|
||||
try:
|
||||
dtstart=datetime.strptime(prop['DTSTART;VALUE=DATE'], '%Y%m%d')
|
||||
dtend=datetime.strptime(prop['DTEND;VALUE=DATE'], '%Y%m%d')
|
||||
now=datetime.now()
|
||||
if dtstart <=now and dtend>=now:
|
||||
prop_out['start']=dtstart.strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||
prop_out['end']=dtend.strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||
prop_out['summary']=prop['SUMMARY'].split('(')[0].strip()
|
||||
prop_out['callsign']=prop['SUMMARY'].split('(',1)[1].split(')',1)[0]
|
||||
prop_out['description']=prop['DESCRIPTION'].replace('\\', '')
|
||||
dtstart = datetime.strptime(prop["DTSTART;VALUE=DATE"], "%Y%m%d")
|
||||
dtend = datetime.strptime(prop["DTEND;VALUE=DATE"], "%Y%m%d")
|
||||
now = datetime.now()
|
||||
if dtstart <= now and dtend >= now:
|
||||
prop_out["start"] = dtstart.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
prop_out["end"] = dtend.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
prop_out["summary"] = prop["SUMMARY"].split("(")[0].strip()
|
||||
prop_out["callsign"] = prop["SUMMARY"].split("(", 1)[1].split(")", 1)[0]
|
||||
prop_out["description"] = prop["DESCRIPTION"].replace("\\", "")
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return prop_out
|
||||
|
||||
#TODO: url from conf parameter
|
||||
|
||||
# TODO: url from conf parameter
|
||||
|
||||
|
||||
def get_adxo_events():
|
||||
url = 'http://dxcal.kj4z.com/dxcal'
|
||||
line_num=0
|
||||
event_num=0
|
||||
url = "http://dxcal.kj4z.com/dxcal"
|
||||
line_num = 0
|
||||
event_num = 0
|
||||
try:
|
||||
logging.info('connection to: '+url)
|
||||
req=requests.get(url)
|
||||
events=[]
|
||||
prop=dict()
|
||||
prop_name=''
|
||||
logging.info("connection to: " + url)
|
||||
req = requests.get(url)
|
||||
events = []
|
||||
prop = dict()
|
||||
prop_name = ""
|
||||
with tempfile.TemporaryFile() as temp:
|
||||
temp.write(req.content)
|
||||
temp.seek(0)
|
||||
lines=temp.readlines()
|
||||
lines = temp.readlines()
|
||||
for line_bytes in lines:
|
||||
line=line_bytes.decode()
|
||||
line_num+=1
|
||||
current_line_array=line.strip().split(':', 1)
|
||||
if current_line_array[0]=='BEGIN':
|
||||
if current_line_array[1]=='VCALENDAR':
|
||||
prop={}
|
||||
if current_line_array[1]=='VEVENT':
|
||||
event_num+=1
|
||||
prop={}
|
||||
line = line_bytes.decode()
|
||||
line_num += 1
|
||||
current_line_array = line.strip().split(":", 1)
|
||||
if current_line_array[0] == "BEGIN":
|
||||
if current_line_array[1] == "VCALENDAR":
|
||||
prop = {}
|
||||
if current_line_array[1] == "VEVENT":
|
||||
event_num += 1
|
||||
prop = {}
|
||||
else:
|
||||
if current_line_array[0]=='END':
|
||||
if current_line_array[1]=='VCALENDAR':
|
||||
if current_line_array[0] == "END":
|
||||
if current_line_array[1] == "VCALENDAR":
|
||||
pass
|
||||
if current_line_array[1]=='VEVENT':
|
||||
prop=format_line(prop)
|
||||
if current_line_array[1] == "VEVENT":
|
||||
prop = format_line(prop)
|
||||
if prop:
|
||||
events.append(prop)
|
||||
else:
|
||||
if len(current_line_array)>1:
|
||||
prop_name=current_line_array[0]
|
||||
prop[prop_name]=current_line_array[1]
|
||||
if len(current_line_array) > 1:
|
||||
prop_name = current_line_array[0]
|
||||
prop[prop_name] = current_line_array[1]
|
||||
else:
|
||||
if len(prop_name)>0:
|
||||
prop[prop_name]=prop[prop_name]+current_line_array[0]
|
||||
if len(prop_name) > 0:
|
||||
prop[prop_name] = (
|
||||
prop[prop_name] + current_line_array[0]
|
||||
)
|
||||
|
||||
logging.debug('number of line reads: '+str(line_num))
|
||||
logging.info('number ADXO events: '+str(event_num))
|
||||
logging.debug("number of line reads: " + str(line_num))
|
||||
logging.info("number ADXO events: " + str(event_num))
|
||||
return events
|
||||
except Exception as e1:
|
||||
logging.error(e1)
|
||||
|
357
lib/config.py
357
lib/config.py
@ -1,55 +1,59 @@
|
||||
#*************************************************************************************
|
||||
# *************************************************************************************
|
||||
# CLI Utility used for manage configuration file
|
||||
#*************************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
# *************************************************************************************
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import os
|
||||
import os.path
|
||||
from os import path
|
||||
import json
|
||||
|
||||
configs = [('mycallsign','Callsign_______________: '),
|
||||
('mysql/host','MySql host_____________: '),
|
||||
('mysql/db','MySql database_________: '),
|
||||
('mysql/user','MySql user_____________: '),
|
||||
('mysql/passwd','MySql password_________: '),
|
||||
('timer/interval','Spot page refresh(ms)__: ' ),
|
||||
('plot_refresh_timer/interval','Plot page refresh(ms)__: '),
|
||||
('mail','Mail address___________: '),
|
||||
('mail_token','token google 2FA auth__: '),
|
||||
('telnet','Telnet address_________: '),
|
||||
('enable_cq_filter','Enable cq filter______: ')
|
||||
]
|
||||
configs = [
|
||||
("mycallsign", "Callsign_______________: "),
|
||||
("mysql/host", "MySql host_____________: "),
|
||||
("mysql/db", "MySql database_________: "),
|
||||
("mysql/user", "MySql user_____________: "),
|
||||
("mysql/passwd", "MySql password_________: "),
|
||||
("timer/interval", "Spot page refresh(ms)__: "),
|
||||
("plot_refresh_timer/interval", "Plot page refresh(ms)__: "),
|
||||
("mail", "Mail address___________: "),
|
||||
("mail_token", "token google 2FA auth__: "),
|
||||
("telnet", "Telnet address_________: "),
|
||||
("enable_cq_filter", "Enable cq filter______: "),
|
||||
]
|
||||
|
||||
|
||||
class bcolors:
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
HEADER = "\033[95m"
|
||||
OKBLUE = "\033[94m"
|
||||
OKGREEN = "\033[92m"
|
||||
WARNING = "\033[93m"
|
||||
FAIL = "\033[91m"
|
||||
ENDC = "\033[0m"
|
||||
BOLD = "\033[1m"
|
||||
UNDERLINE = "\033[4m"
|
||||
|
||||
TEMPLATE_FILE = '../cfg/config.json.template'
|
||||
USER_FILE = '../cfg/config.json'
|
||||
#search and open the configuration file
|
||||
|
||||
TEMPLATE_FILE = "../cfg/config.json.template"
|
||||
USER_FILE = "../cfg/config.json"
|
||||
# search and open the configuration file
|
||||
def get_cfg_file(template):
|
||||
if template:
|
||||
cfg_file = TEMPLATE_FILE
|
||||
if not path.exists(cfg_file):
|
||||
print ('file not found')
|
||||
cfg_file = ''
|
||||
print("file not found")
|
||||
cfg_file = ""
|
||||
else:
|
||||
cfg_file = USER_FILE
|
||||
if not path.exists(cfg_file):
|
||||
cfg_file = TEMPLATE_FILE
|
||||
if not path.exists(cfg_file):
|
||||
cfg_file = ''
|
||||
cfg_file = ""
|
||||
|
||||
print('Configuration file loaded from: '+cfg_file)
|
||||
print("Configuration file loaded from: " + cfg_file)
|
||||
return cfg_file
|
||||
|
||||
#covert file in json
|
||||
|
||||
# covert file in json
|
||||
def get_cfg_json(f):
|
||||
if f:
|
||||
with open(f) as json_data_file:
|
||||
@ -59,10 +63,11 @@ def get_cfg_json(f):
|
||||
|
||||
return cfg
|
||||
|
||||
#read a single value from json
|
||||
def get_cfg_value(cfg,key):
|
||||
|
||||
k_arr = key.split('/')
|
||||
# read a single value from json
|
||||
def get_cfg_value(cfg, key):
|
||||
|
||||
k_arr = key.split("/")
|
||||
l_arr = len(k_arr)
|
||||
|
||||
try:
|
||||
@ -71,228 +76,254 @@ def get_cfg_value(cfg,key):
|
||||
elif l_arr == 2:
|
||||
val = cfg[k_arr[0]][k_arr[1]]
|
||||
except KeyError:
|
||||
val=''
|
||||
val = ""
|
||||
|
||||
return val
|
||||
|
||||
def set_cfg_value(cfg,key,val):
|
||||
k_arr = key.split('/')
|
||||
|
||||
def set_cfg_value(cfg, key, val):
|
||||
k_arr = key.split("/")
|
||||
l_arr = len(k_arr)
|
||||
|
||||
try:
|
||||
if l_arr == 1:
|
||||
cfg[k_arr[0]]=val
|
||||
cfg[k_arr[0]] = val
|
||||
elif l_arr == 2:
|
||||
cfg[k_arr[0]][k_arr[1]]=val
|
||||
cfg[k_arr[0]][k_arr[1]] = val
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return cfg
|
||||
|
||||
def style_field(lbl,val):
|
||||
return lbl+bcolors.BOLD + str(val) + bcolors.ENDC
|
||||
|
||||
def show_menu(cfg,key):
|
||||
menu=get_cfg_value(cfg,'menu/menu_list')
|
||||
i=-1
|
||||
def style_field(lbl, val):
|
||||
return lbl + bcolors.BOLD + str(val) + bcolors.ENDC
|
||||
|
||||
|
||||
def show_menu(cfg, key):
|
||||
menu = get_cfg_value(cfg, "menu/menu_list")
|
||||
i = -1
|
||||
for element in menu:
|
||||
i+=1
|
||||
print(style_field(str(i)+'. external: ',str(element['external']))+style_field(', label: ',element['label']))
|
||||
print(style_field(' link____: ',element['link']))
|
||||
i += 1
|
||||
print(
|
||||
style_field(str(i) + ". external: ", str(element["external"]))
|
||||
+ style_field(", label: ", element["label"])
|
||||
)
|
||||
print(style_field(" link____: ", element["link"]))
|
||||
print()
|
||||
return
|
||||
|
||||
|
||||
def help_list():
|
||||
print ()
|
||||
print (' h: help')
|
||||
print (' vc: view config.')
|
||||
print (' ec: edit config.')
|
||||
print (' vm: view menu')
|
||||
print (' em: edit menu')
|
||||
print (' s: save')
|
||||
print (' t: load config. from template')
|
||||
print ()
|
||||
print (' x: exit')
|
||||
print ()
|
||||
print()
|
||||
print(" h: help")
|
||||
print(" vc: view config.")
|
||||
print(" ec: edit config.")
|
||||
print(" vm: view menu")
|
||||
print(" em: edit menu")
|
||||
print(" s: save")
|
||||
print(" t: load config. from template")
|
||||
print()
|
||||
print(" x: exit")
|
||||
print()
|
||||
return
|
||||
|
||||
|
||||
def help_menu_edit():
|
||||
print ()
|
||||
print (' n: new menu entry')
|
||||
print (' d: delete menu entry')
|
||||
print (' e: edit menu entry')
|
||||
print ()
|
||||
print (' x: exit')
|
||||
print ()
|
||||
print()
|
||||
print(" n: new menu entry")
|
||||
print(" d: delete menu entry")
|
||||
print(" e: edit menu entry")
|
||||
print()
|
||||
print(" x: exit")
|
||||
print()
|
||||
return
|
||||
|
||||
def view(cfg,t):
|
||||
if t == 'c':
|
||||
i=0
|
||||
|
||||
def view(cfg, t):
|
||||
if t == "c":
|
||||
i = 0
|
||||
for element in configs:
|
||||
(key, lbl)=element
|
||||
print(style_field(str(i)+'. '+lbl,str(get_cfg_value(cfg,key))))
|
||||
i+=1
|
||||
elif t == 'm':
|
||||
print ('Menu:')
|
||||
show_menu(cfg,'menu/menu_list')
|
||||
(key, lbl) = element
|
||||
print(style_field(str(i) + ". " + lbl, str(get_cfg_value(cfg, key))))
|
||||
i += 1
|
||||
elif t == "m":
|
||||
print("Menu:")
|
||||
show_menu(cfg, "menu/menu_list")
|
||||
|
||||
print()
|
||||
return
|
||||
|
||||
|
||||
def user_input(caption):
|
||||
return input(caption)
|
||||
|
||||
|
||||
def edit_config(cfg):
|
||||
view(cfg,'c')
|
||||
inp=''
|
||||
while inp!='x':
|
||||
inp=str(user_input ('Type the number of config. you would to edit, x for end: ')).lower()
|
||||
view(cfg, "c")
|
||||
inp = ""
|
||||
while inp != "x":
|
||||
inp = str(
|
||||
user_input("Type the number of config. you would to edit, x for end: ")
|
||||
).lower()
|
||||
if inp.isdigit():
|
||||
inp=int(inp)
|
||||
inp = int(inp)
|
||||
try:
|
||||
(key, lbl)=configs[inp]
|
||||
print(style_field(lbl,get_cfg_value(cfg,key)))
|
||||
val=str(user_input ('Enter new value, [ENTER] for nothing: '))
|
||||
(key, lbl) = configs[inp]
|
||||
print(style_field(lbl, get_cfg_value(cfg, key)))
|
||||
val = str(user_input("Enter new value, [ENTER] for nothing: "))
|
||||
except IndexError:
|
||||
print ('configuration not found!')
|
||||
print("configuration not found!")
|
||||
finally:
|
||||
if val!='x' and val !='':
|
||||
cfg=set_cfg_value(cfg,key,val)
|
||||
if val != "x" and val != "":
|
||||
cfg = set_cfg_value(cfg, key, val)
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def menu_delete_entry(cfg):
|
||||
view(cfg,'m')
|
||||
inp=''
|
||||
while inp!='x':
|
||||
inp=str(user_input ('Choose the menu you would to delete, x for end: ')).lower()
|
||||
view(cfg, "m")
|
||||
inp = ""
|
||||
while inp != "x":
|
||||
inp = str(
|
||||
user_input("Choose the menu you would to delete, x for end: ")
|
||||
).lower()
|
||||
if inp.isdigit():
|
||||
inp=int(inp)
|
||||
element = cfg['menu']['menu_list']
|
||||
inp = int(inp)
|
||||
element = cfg["menu"]["menu_list"]
|
||||
try:
|
||||
del element[inp]
|
||||
cfg['menu']['menu_list']=element
|
||||
cfg["menu"]["menu_list"] = element
|
||||
except IndexError:
|
||||
print ('menu entry not found!')
|
||||
print("menu entry not found!")
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def is_external(val):
|
||||
return val == 'y'
|
||||
return val == "y"
|
||||
|
||||
def menu_input_entry(entry,new_entry):
|
||||
|
||||
def menu_input_entry(entry, new_entry):
|
||||
if not new_entry:
|
||||
print('label old value: '+entry['label'])
|
||||
print("label old value: " + entry["label"])
|
||||
|
||||
label=str(user_input('label new value: '))
|
||||
label = str(user_input("label new value: "))
|
||||
if not new_entry:
|
||||
print('link old value: '+entry['link'])
|
||||
print("link old value: " + entry["link"])
|
||||
|
||||
link=str(user_input('link value: '))
|
||||
link = str(user_input("link value: "))
|
||||
if not new_entry:
|
||||
print('external old value: '+str(entry['external']))
|
||||
print("external old value: " + str(entry["external"]))
|
||||
|
||||
external = ''
|
||||
while external != 'y' and external != 'n':
|
||||
external=str(user_input('open link external [y/n]: ')).lower()
|
||||
external = ""
|
||||
while external != "y" and external != "n":
|
||||
external = str(user_input("open link external [y/n]: ")).lower()
|
||||
|
||||
# if external == 'y':
|
||||
# external = True
|
||||
# else:
|
||||
# external = False
|
||||
external=is_external(external)
|
||||
# if external == 'y':
|
||||
# external = True
|
||||
# else:
|
||||
# external = False
|
||||
external = is_external(external)
|
||||
|
||||
entry['label']=label
|
||||
entry['link']=link
|
||||
entry['external']=external
|
||||
entry["label"] = label
|
||||
entry["link"] = link
|
||||
entry["external"] = external
|
||||
|
||||
return entry
|
||||
|
||||
|
||||
def menu_edit_entry(cfg):
|
||||
view(cfg,'m')
|
||||
inp=''
|
||||
while inp!='x':
|
||||
inp=str(user_input ('Choose the menu you would to edit, X for end: ')).lower()
|
||||
view(cfg, "m")
|
||||
inp = ""
|
||||
while inp != "x":
|
||||
inp = str(user_input("Choose the menu you would to edit, X for end: ")).lower()
|
||||
if inp.isdigit():
|
||||
inp=int(inp)
|
||||
element = cfg['menu']['menu_list']
|
||||
inp = int(inp)
|
||||
element = cfg["menu"]["menu_list"]
|
||||
try:
|
||||
element[inp]=menu_input_entry(element[inp],False)
|
||||
cfg['menu']['menu_list']=element
|
||||
element[inp] = menu_input_entry(element[inp], False)
|
||||
cfg["menu"]["menu_list"] = element
|
||||
except IndexError:
|
||||
print ('menu entry not found!')
|
||||
print("menu entry not found!")
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def menu_new_entry(cfg):
|
||||
view(cfg,'m')
|
||||
inp=''
|
||||
view(cfg, "m")
|
||||
inp = ""
|
||||
valid = False
|
||||
entry=menu_input_entry({"label":"", "link": "", "external": False},True)
|
||||
entry = menu_input_entry({"label": "", "link": "", "external": False}, True)
|
||||
while not valid:
|
||||
inp=str(user_input ('Enter the position number of your menu entry, X for end: '))
|
||||
inp = str(
|
||||
user_input("Enter the position number of your menu entry, X for end: ")
|
||||
)
|
||||
if inp.isdigit():
|
||||
inp=int(inp)
|
||||
if inp > len(cfg['menu']['menu_list']):
|
||||
print('position not valid!')
|
||||
inp = int(inp)
|
||||
if inp > len(cfg["menu"]["menu_list"]):
|
||||
print("position not valid!")
|
||||
valid = False
|
||||
elif inp == len(cfg['menu']['menu_list']):
|
||||
cfg['menu']['menu_list'].append(entry)
|
||||
elif inp == len(cfg["menu"]["menu_list"]):
|
||||
cfg["menu"]["menu_list"].append(entry)
|
||||
valid = True
|
||||
else:
|
||||
cfg['menu']['menu_list'].insert(inp,entry)
|
||||
cfg["menu"]["menu_list"].insert(inp, entry)
|
||||
valid = True
|
||||
else:
|
||||
valid = False
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def edit_menu(cfg):
|
||||
view(cfg,'m')
|
||||
inp=''
|
||||
while inp!='x':
|
||||
view(cfg, "m")
|
||||
inp = ""
|
||||
while inp != "x":
|
||||
help_menu_edit()
|
||||
inp=str(user_input ('Edit menu> make your choiche: ')).lower()
|
||||
if (inp == 'n'):
|
||||
cfg=menu_new_entry(cfg)
|
||||
elif inp == 'd':
|
||||
cfg=menu_delete_entry(cfg)
|
||||
elif inp == 'e':
|
||||
cfg=menu_edit_entry(cfg)
|
||||
inp = str(user_input("Edit menu> make your choiche: ")).lower()
|
||||
if inp == "n":
|
||||
cfg = menu_new_entry(cfg)
|
||||
elif inp == "d":
|
||||
cfg = menu_delete_entry(cfg)
|
||||
elif inp == "e":
|
||||
cfg = menu_edit_entry(cfg)
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def save_cfg(cfg):
|
||||
with open(USER_FILE, 'w') as outfile:
|
||||
json.dump(cfg, outfile,indent=4)
|
||||
print ('configuration saved to: '+USER_FILE)
|
||||
with open(USER_FILE, "w") as outfile:
|
||||
json.dump(cfg, outfile, indent=4)
|
||||
print("configuration saved to: " + USER_FILE)
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
print()
|
||||
print('*** DxSpider configuration ***')
|
||||
finput=get_cfg_file(False)
|
||||
print("*** DxSpider configuration ***")
|
||||
finput = get_cfg_file(False)
|
||||
help_list()
|
||||
cfg=get_cfg_json(finput)
|
||||
inp = ''
|
||||
while inp != 'x' and inp != 'exit':
|
||||
inp = str(user_input('Main> make your choiche: ')).lower()
|
||||
if (inp == 'h' or inp =='?' or inp =='help'):
|
||||
cfg = get_cfg_json(finput)
|
||||
inp = ""
|
||||
while inp != "x" and inp != "exit":
|
||||
inp = str(user_input("Main> make your choiche: ")).lower()
|
||||
if inp == "h" or inp == "?" or inp == "help":
|
||||
help_list()
|
||||
elif inp == 'vc':
|
||||
view(cfg,'c')
|
||||
elif inp == 'vm':
|
||||
view(cfg,'m')
|
||||
elif inp == 'ec':
|
||||
cfg=edit_config(cfg)
|
||||
elif inp == 'em':
|
||||
cfg=edit_menu(cfg)
|
||||
elif inp == 's' or inp == 'save':
|
||||
elif inp == "vc":
|
||||
view(cfg, "c")
|
||||
elif inp == "vm":
|
||||
view(cfg, "m")
|
||||
elif inp == "ec":
|
||||
cfg = edit_config(cfg)
|
||||
elif inp == "em":
|
||||
cfg = edit_menu(cfg)
|
||||
elif inp == "s" or inp == "save":
|
||||
save_cfg(cfg)
|
||||
elif inp == 't':
|
||||
finput=get_cfg_file(True)
|
||||
cfg=get_cfg_json(finput)
|
||||
elif inp == "t":
|
||||
finput = get_cfg_file(True)
|
||||
cfg = get_cfg_json(finput)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
379
lib/cty.py
379
lib/cty.py
@ -1,7 +1,7 @@
|
||||
#*************************************************************************************
|
||||
# *************************************************************************************
|
||||
# Module used to download cty.dat country file and search callsign in it
|
||||
#*************************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
# *************************************************************************************
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import requests
|
||||
import logging
|
||||
import os
|
||||
@ -10,124 +10,147 @@ from threading import Timer
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
||||
#TODO: url from conf parameter
|
||||
url = 'https://www.country-files.com/cty/cty_wt_mod.dat'
|
||||
cty_local=os.path.dirname(__file__)+'/../cfg/cty_wt_mod.dat'
|
||||
country_file=os.path.dirname(__file__)+'/../cfg/country.json'
|
||||
#-------------------------------------------------------------------------------------
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||
datefmt="%m/%d/%Y %I:%M:%S",
|
||||
)
|
||||
# TODO: url from conf parameter
|
||||
url = "https://www.country-files.com/cty/cty_wt_mod.dat"
|
||||
cty_local = os.path.dirname(__file__) + "/../static/data/cty_wt_mod.dat"
|
||||
country_file = os.path.dirname(__file__) + "/../cfg/country.json"
|
||||
# -------------------------------------------------------------------------------------
|
||||
# download country files cty.dat
|
||||
#-------------------------------------------------------------------------------------
|
||||
def download_cty(url,cty_local):
|
||||
# -------------------------------------------------------------------------------------
|
||||
def download_cty(url, cty_local):
|
||||
try:
|
||||
logging.info('connection to: '+url)
|
||||
req=requests.get(url)
|
||||
f=open(cty_local,'wb')
|
||||
logging.info("connection to: " + url)
|
||||
req = requests.get(url)
|
||||
f = open(cty_local, "wb")
|
||||
f.write(req.content)
|
||||
f.close()
|
||||
logging.info('cty file saved in: '+cty_local)
|
||||
logging.info("cty file saved in: " + cty_local)
|
||||
return 0
|
||||
|
||||
except Exception as e1:
|
||||
logging.error(e1)
|
||||
return 1
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# get age of a file in days
|
||||
#-------------------------------------------------------------------------------------
|
||||
def file_age_in_days(pathname):
|
||||
return (time.time() - os.stat(pathname).st_ctime)/(24*3600)
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
# get age of a file in days
|
||||
# -------------------------------------------------------------------------------------
|
||||
def file_age_in_days(pathname):
|
||||
return (time.time() - os.stat(pathname).st_ctime) / (24 * 3600)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# manage file cty.dat
|
||||
#-------------------------------------------------------------------------------------
|
||||
def get_cty(url,local):
|
||||
# -------------------------------------------------------------------------------------
|
||||
def get_cty(url, local):
|
||||
if os.path.isfile(local):
|
||||
age=file_age_in_days(local)
|
||||
if age>7:
|
||||
logging.info(cty_local+' too old ('+str(round(age,0))+' days): proceding to download it')
|
||||
return download_cty(url,local)
|
||||
# else:
|
||||
# logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary to download it')
|
||||
# return 0
|
||||
logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary to download it')
|
||||
age = file_age_in_days(local)
|
||||
if age > 7:
|
||||
logging.info(
|
||||
cty_local
|
||||
+ " too old ("
|
||||
+ str(round(age, 0))
|
||||
+ " days): proceding to download it"
|
||||
)
|
||||
return download_cty(url, local)
|
||||
# else:
|
||||
# logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary to download it')
|
||||
# return 0
|
||||
logging.info(
|
||||
cty_local
|
||||
+ " updated ("
|
||||
+ str(round(age, 0))
|
||||
+ " days), is not necessary to download it"
|
||||
)
|
||||
return 0
|
||||
# else:
|
||||
# logging.info(cty_local+' not present: proceding to download it')
|
||||
# return download_cty(url,local)
|
||||
|
||||
logging.info(cty_local+' not present: proceding to download it')
|
||||
return download_cty(url,local)
|
||||
logging.info(cty_local + " not present: proceding to download it")
|
||||
return download_cty(url, local)
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# parsing alias and get exceptions
|
||||
#-------------------------------------------------------------------------------------
|
||||
def parse_alias(alias,master):
|
||||
# -------------------------------------------------------------------------------------
|
||||
def parse_alias(alias, master):
|
||||
|
||||
try:
|
||||
#create a dictionary of array, with start and end position of each exception
|
||||
find_dict={}
|
||||
find_dict['pos_cq']=[alias.find('('),alias.find(')')]
|
||||
find_dict['pos_itu']=[alias.find('['),alias.find(']')]
|
||||
find_dict['pos_lat_lon']=[alias.find('<'),alias.find('>')]
|
||||
find_dict['pos_continent']=[alias.find('{'),alias.find('}')]
|
||||
find_dict['pos_time']=[alias.find('~'),alias[:alias.find('~')].find('~')]
|
||||
# create a dictionary of array, with start and end position of each exception
|
||||
find_dict = {}
|
||||
find_dict["pos_cq"] = [alias.find("("), alias.find(")")]
|
||||
find_dict["pos_itu"] = [alias.find("["), alias.find("]")]
|
||||
find_dict["pos_lat_lon"] = [alias.find("<"), alias.find(">")]
|
||||
find_dict["pos_continent"] = [alias.find("{"), alias.find("}")]
|
||||
find_dict["pos_time"] = [alias.find("~"), alias[: alias.find("~")].find("~")]
|
||||
|
||||
first=9999
|
||||
parsed={}
|
||||
first = 9999
|
||||
parsed = {}
|
||||
|
||||
#assign default values from master callsing
|
||||
parsed["country"]=master["country"]
|
||||
parsed["cq"]=master["cq"]
|
||||
parsed["itu"]=master["itu"]
|
||||
parsed["continent"]=master["continent"]
|
||||
parsed["lat"]=master["lat"]
|
||||
parsed["lon"]=master["lon"]
|
||||
parsed["time_loc"]=master["time_loc"]
|
||||
parsed["full"]=master["full"]
|
||||
parsed["darc_waedc"]=master["darc_waedc"]
|
||||
# assign default values from master callsing
|
||||
parsed["country"] = master["country"]
|
||||
parsed["cq"] = master["cq"]
|
||||
parsed["itu"] = master["itu"]
|
||||
parsed["continent"] = master["continent"]
|
||||
parsed["lat"] = master["lat"]
|
||||
parsed["lon"] = master["lon"]
|
||||
parsed["time_loc"] = master["time_loc"]
|
||||
parsed["full"] = master["full"]
|
||||
parsed["darc_waedc"] = master["darc_waedc"]
|
||||
|
||||
#extract override cq
|
||||
if find_dict['pos_cq'][0]>=0:
|
||||
parsed["cq"]=alias[find_dict['pos_cq'][0]+1:find_dict['pos_cq'][1]]
|
||||
if find_dict['pos_cq'][0] < first:
|
||||
first=find_dict['pos_cq'][0]
|
||||
# extract override cq
|
||||
if find_dict["pos_cq"][0] >= 0:
|
||||
parsed["cq"] = alias[find_dict["pos_cq"][0] + 1 : find_dict["pos_cq"][1]]
|
||||
if find_dict["pos_cq"][0] < first:
|
||||
first = find_dict["pos_cq"][0]
|
||||
|
||||
#extract override itu
|
||||
if find_dict['pos_itu'][0]>=0:
|
||||
parsed["itu"]=alias[find_dict['pos_itu'][0]+1:find_dict['pos_itu'][1]]
|
||||
if find_dict['pos_itu'][0] < first:
|
||||
first=find_dict['pos_itu'][0]
|
||||
# extract override itu
|
||||
if find_dict["pos_itu"][0] >= 0:
|
||||
parsed["itu"] = alias[find_dict["pos_itu"][0] + 1 : find_dict["pos_itu"][1]]
|
||||
if find_dict["pos_itu"][0] < first:
|
||||
first = find_dict["pos_itu"][0]
|
||||
|
||||
#extract override lat_lon
|
||||
if find_dict['pos_lat_lon'][0]>=0:
|
||||
lat_lon=alias[find_dict['pos_lat_lon'][0]+1:find_dict['pos_lat_lon'][1]]
|
||||
parsed["lat"]=lat_lon[0:].split('/')[0]
|
||||
parsed["lon"]=lat_lon[:len(lat_lon)].split('/')[1]
|
||||
if find_dict['pos_lat_lon'][0] < first:
|
||||
first=find_dict['pos_lat_lon'][0]
|
||||
# extract override lat_lon
|
||||
if find_dict["pos_lat_lon"][0] >= 0:
|
||||
lat_lon = alias[
|
||||
find_dict["pos_lat_lon"][0] + 1 : find_dict["pos_lat_lon"][1]
|
||||
]
|
||||
parsed["lat"] = lat_lon[0:].split("/")[0]
|
||||
parsed["lon"] = lat_lon[: len(lat_lon)].split("/")[1]
|
||||
if find_dict["pos_lat_lon"][0] < first:
|
||||
first = find_dict["pos_lat_lon"][0]
|
||||
|
||||
#extract override continent
|
||||
if find_dict['pos_continent'][0]>=0:
|
||||
parsed["continent"]=alias[find_dict['pos_continent'][0]+1:find_dict['pos_continent'][1]]
|
||||
if find_dict['pos_continent'][0] < first:
|
||||
first=find_dict['pos_continent'][0]
|
||||
# extract override continent
|
||||
if find_dict["pos_continent"][0] >= 0:
|
||||
parsed["continent"] = alias[
|
||||
find_dict["pos_continent"][0] + 1 : find_dict["pos_continent"][1]
|
||||
]
|
||||
if find_dict["pos_continent"][0] < first:
|
||||
first = find_dict["pos_continent"][0]
|
||||
|
||||
#extract override time
|
||||
if find_dict['pos_time'][0]>=0:
|
||||
parsed["time_loc"]=alias[find_dict['pos_time'][0]+1:find_dict['pos_time'][1]]
|
||||
if find_dict['pos_time'][0] < first:
|
||||
first=find_dict['pos_time'][0]
|
||||
# extract override time
|
||||
if find_dict["pos_time"][0] >= 0:
|
||||
parsed["time_loc"] = alias[
|
||||
find_dict["pos_time"][0] + 1 : find_dict["pos_time"][1]
|
||||
]
|
||||
if find_dict["pos_time"][0] < first:
|
||||
first = find_dict["pos_time"][0]
|
||||
|
||||
#extract callsign
|
||||
callsing=alias[:first].upper()
|
||||
if callsing.startswith('='):
|
||||
parsed["full"]='y'
|
||||
callsing=callsing[1:]
|
||||
# extract callsign
|
||||
callsing = alias[:first].upper()
|
||||
if callsing.startswith("="):
|
||||
parsed["full"] = "y"
|
||||
callsing = callsing[1:]
|
||||
|
||||
if callsing.startswith('*'):
|
||||
parsed["darc_waedc"]='y'
|
||||
callsing=callsing[1:]
|
||||
if callsing.startswith("*"):
|
||||
parsed["darc_waedc"] = "y"
|
||||
callsing = callsing[1:]
|
||||
|
||||
return callsing, parsed
|
||||
|
||||
@ -138,38 +161,42 @@ def parse_alias(alias,master):
|
||||
logging.error(alias)
|
||||
return -1
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# load file from configuration, containing all world country, with related ISO codes
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
def load_country():
|
||||
with open(country_file) as json_country:
|
||||
return json.load(json_country)
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# search for ISO code, transcoding the country description
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
def add_country(table):
|
||||
country_data=load_country()
|
||||
country_data = load_country()
|
||||
for key, value in table.items():
|
||||
found=0
|
||||
for i in country_data['country_codes']:
|
||||
if i['desc'].upper()==value['country'].upper():
|
||||
value["iso"]=i["ISO"]
|
||||
value["wpx"]=i["WPX"]
|
||||
found=1
|
||||
found = 0
|
||||
for i in country_data["country_codes"]:
|
||||
if i["desc"].upper() == value["country"].upper():
|
||||
value["iso"] = i["ISO"]
|
||||
value["wpx"] = i["WPX"]
|
||||
found = 1
|
||||
break
|
||||
if found==0:
|
||||
logging.warning('country "'+value['country']+'" not found in cfg/country.json')
|
||||
if found == 0:
|
||||
logging.warning(
|
||||
'country "' + value["country"] + '" not found in cfg/country.json'
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
class prefix_table:
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
# init of the class
|
||||
# - download file cty.dat
|
||||
# - parse file and create prefix_master with all prefixies and attributes
|
||||
#.....................................................................................
|
||||
# .....................................................................................
|
||||
# CTY.DAT Format
|
||||
#
|
||||
# reference: https://www.country-files.com/cty-dat-format/
|
||||
@ -203,84 +230,89 @@ class prefix_table:
|
||||
# {aa} Override Continent
|
||||
# ~#~ Override local time offset from GMT
|
||||
#
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
|
||||
global prefix_master
|
||||
prefix_master=dict()
|
||||
prefix_master = dict()
|
||||
initialization()
|
||||
return
|
||||
|
||||
global initialization
|
||||
|
||||
def initialization():
|
||||
refresh()
|
||||
global timer
|
||||
timer = Timer(3600*24,initialization) #try to refresh once a day
|
||||
timer = Timer(3600 * 24, initialization) # try to refresh once a day
|
||||
timer.start()
|
||||
return
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
# refresh data
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
global refresh
|
||||
|
||||
def refresh():
|
||||
|
||||
logging.info('CTY: start initialization')
|
||||
if get_cty(url,cty_local)>0:
|
||||
logging.error('there is a problem during downloading country files!')
|
||||
logging.info('continue with previous file')
|
||||
logging.info('check the connectivity, or put manually the file '+cty_local)
|
||||
line_num=0
|
||||
line_num_valid=0
|
||||
entities_number=0
|
||||
data=''
|
||||
table=[]
|
||||
logging.info("CTY: start initialization")
|
||||
if get_cty(url, cty_local) > 0:
|
||||
logging.error("there is a problem during downloading country files!")
|
||||
logging.info("continue with previous file")
|
||||
logging.info(
|
||||
"check the connectivity, or put manually the file " + cty_local
|
||||
)
|
||||
line_num = 0
|
||||
line_num_valid = 0
|
||||
entities_number = 0
|
||||
data = ""
|
||||
table = []
|
||||
prefix_master.clear()
|
||||
try:
|
||||
with open(cty_local, 'r') as f:
|
||||
with open(cty_local, "r") as f:
|
||||
for line in f:
|
||||
line_num+=1
|
||||
li=line.strip()
|
||||
#remove comments
|
||||
line_num += 1
|
||||
li = line.strip()
|
||||
# remove comments
|
||||
if not li.startswith("#"):
|
||||
line_num_valid+=1
|
||||
data+=li
|
||||
logging.info('number of lines reads: '+str(line_num))
|
||||
logging.info('number of valid lines: '+str(line_num_valid))
|
||||
line_num_valid += 1
|
||||
data += li
|
||||
logging.info("number of lines reads: " + str(line_num))
|
||||
logging.info("number of valid lines: " + str(line_num_valid))
|
||||
|
||||
#split in array of lines terminated with semicolon
|
||||
table=data.split(';')
|
||||
for i,item_table in enumerate(table):
|
||||
row=item_table.split(':')
|
||||
#remove trailing spaces and uppercasing
|
||||
row = [x.strip(' ') for x in row]
|
||||
if len(row)==9:
|
||||
#if the row is corret put the row in a master prefix dictionary
|
||||
entities_number+=1
|
||||
single_prefix={}
|
||||
single_prefix["country"]=row[0]
|
||||
single_prefix["cq"]=row[1]
|
||||
single_prefix["itu"]=row[2]
|
||||
single_prefix["continent"]=row[3]
|
||||
single_prefix["lat"]=row[4]
|
||||
single_prefix["lon"]=row[5]
|
||||
single_prefix["time_loc"]=row[6]
|
||||
single_prefix["full"]='n'
|
||||
single_prefix["darc_waedc"]='n'
|
||||
prefix_master[row[7].upper()]=single_prefix
|
||||
#managing sub-prefixies
|
||||
sub_prefixies=row[8].split(',')
|
||||
# split in array of lines terminated with semicolon
|
||||
table = data.split(";")
|
||||
for i, item_table in enumerate(table):
|
||||
row = item_table.split(":")
|
||||
# remove trailing spaces and uppercasing
|
||||
row = [x.strip(" ") for x in row]
|
||||
if len(row) == 9:
|
||||
# if the row is corret put the row in a master prefix dictionary
|
||||
entities_number += 1
|
||||
single_prefix = {}
|
||||
single_prefix["country"] = row[0]
|
||||
single_prefix["cq"] = row[1]
|
||||
single_prefix["itu"] = row[2]
|
||||
single_prefix["continent"] = row[3]
|
||||
single_prefix["lat"] = row[4]
|
||||
single_prefix["lon"] = row[5]
|
||||
single_prefix["time_loc"] = row[6]
|
||||
single_prefix["full"] = "n"
|
||||
single_prefix["darc_waedc"] = "n"
|
||||
prefix_master[row[7].upper()] = single_prefix
|
||||
# managing sub-prefixies
|
||||
sub_prefixies = row[8].split(",")
|
||||
for sb in sub_prefixies:
|
||||
values={}
|
||||
callsign, values=parse_alias(sb,single_prefix)
|
||||
prefix_master[callsign]=values
|
||||
values = {}
|
||||
callsign, values = parse_alias(sb, single_prefix)
|
||||
prefix_master[callsign] = values
|
||||
|
||||
logging.info('number of entities: '+str(entities_number))
|
||||
logging.info('number of single alias: '+str(len(prefix_master)))
|
||||
logging.info("number of entities: " + str(entities_number))
|
||||
logging.info("number of single alias: " + str(len(prefix_master)))
|
||||
add_country(prefix_master)
|
||||
logging.info('memory used for prefix: '+str(prefix_master.__sizeof__())+' bytes')
|
||||
logging.info('CTY: initialization complete')
|
||||
logging.info(
|
||||
"memory used for prefix: " + str(prefix_master.__sizeof__()) + " bytes"
|
||||
)
|
||||
logging.info("CTY: initialization complete")
|
||||
return
|
||||
|
||||
except Exception as e1:
|
||||
@ -289,24 +321,23 @@ class prefix_table:
|
||||
logging.error(message)
|
||||
return
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
# find a callsign
|
||||
#-------------------------------------------------------------------------------------
|
||||
def find(self,callsign):
|
||||
# -------------------------------------------------------------------------------------
|
||||
def find(self, callsign):
|
||||
|
||||
try:
|
||||
data=dict()
|
||||
i=len(callsign)
|
||||
callsign=callsign.strip().upper()
|
||||
while i>0:
|
||||
data = dict()
|
||||
i = len(callsign)
|
||||
callsign = callsign.strip().upper()
|
||||
while i > 0:
|
||||
try:
|
||||
data=prefix_master[callsign[:i]]
|
||||
data['match']=callsign[:i]
|
||||
data = prefix_master[callsign[:i]]
|
||||
data["match"] = callsign[:i]
|
||||
return data
|
||||
except KeyError:
|
||||
pass
|
||||
i-=1
|
||||
i -= 1
|
||||
|
||||
except Exception as e1:
|
||||
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
|
||||
@ -314,12 +345,12 @@ class prefix_table:
|
||||
logging.error(message)
|
||||
return data
|
||||
|
||||
#not found
|
||||
data["country"]="unknown country"
|
||||
data["iso"]="xx"
|
||||
# not found
|
||||
data["country"] = "unknown country"
|
||||
data["iso"] = "xx"
|
||||
return data
|
||||
|
||||
def __del__(self):
|
||||
timer.cancel()
|
||||
logging.info('prefix_table destroyed')
|
||||
logging.info("prefix_table destroyed")
|
||||
return
|
||||
|
@ -1,74 +1,78 @@
|
||||
#*************************************************************************************
|
||||
# *************************************************************************************
|
||||
# Module used to interface with telnet cluster and get connected nodes
|
||||
#*************************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
# *************************************************************************************
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
|
||||
import telnetlib
|
||||
import struct
|
||||
import json
|
||||
import logging
|
||||
|
||||
def parse_who(lines):
|
||||
#print(lines.decode('ascii'))
|
||||
|
||||
#create a list o lines and define the structure
|
||||
def parse_who(lines):
|
||||
# print(lines.decode('ascii'))
|
||||
|
||||
# create a list o lines and define the structure
|
||||
lines = lines.splitlines()
|
||||
fmtstring='2x 9s 10s 18s 9s 8s 15s'
|
||||
fmtstring = "2x 9s 10s 18s 9s 8s 15s"
|
||||
fieldstruct = struct.Struct(fmtstring)
|
||||
|
||||
row_headers=('callsign','type','started','name','average_rtt','link')
|
||||
row_headers = ("callsign", "type", "started", "name", "average_rtt", "link")
|
||||
|
||||
#skip first lines and last line
|
||||
payload=[]
|
||||
for i in range(3,len(lines)-1):
|
||||
line=lines[i]
|
||||
ln=len(line)
|
||||
# skip first lines and last line
|
||||
payload = []
|
||||
for i in range(3, len(lines) - 1):
|
||||
line = lines[i]
|
||||
ln = len(line)
|
||||
|
||||
padding=bytes(' ' * (struct.calcsize(fmtstring)-ln),'utf-8')
|
||||
line=(line+padding)
|
||||
padding = bytes(" " * (struct.calcsize(fmtstring) - ln), "utf-8")
|
||||
line = line + padding
|
||||
|
||||
if ln > 10:
|
||||
parse = fieldstruct.unpack_from
|
||||
fields =list(parse(line))
|
||||
fields = list(parse(line))
|
||||
|
||||
for j,item_field in enumerate(fields):
|
||||
for j, item_field in enumerate(fields):
|
||||
try:
|
||||
fields[j]=item_field.decode('utf-8').strip()
|
||||
fields[j] = item_field.decode("utf-8").strip()
|
||||
except AttributeError:
|
||||
print(item_field)
|
||||
payload.append(dict(zip(row_headers,fields)))
|
||||
payload.append(dict(zip(row_headers, fields)))
|
||||
|
||||
# payload = json.dumps(payload)
|
||||
|
||||
return payload
|
||||
|
||||
def who(host,port,user):
|
||||
|
||||
WAIT_FOR = b'dxspider >'
|
||||
TIMEOUT=1
|
||||
res=0
|
||||
def who(host, port, user):
|
||||
|
||||
WAIT_FOR = b"dxspider >"
|
||||
TIMEOUT = 1
|
||||
res = 0
|
||||
|
||||
try:
|
||||
tn = telnetlib.Telnet(host,port,TIMEOUT)
|
||||
tn = telnetlib.Telnet(host, port, TIMEOUT)
|
||||
try:
|
||||
tn.read_until(b"login: ",TIMEOUT)
|
||||
tn.write(user.encode('ascii') + b"\n")
|
||||
res=tn.read_until(WAIT_FOR,TIMEOUT)
|
||||
tn.read_until(b"login: ", TIMEOUT)
|
||||
tn.write(user.encode("ascii") + b"\n")
|
||||
res = tn.read_until(WAIT_FOR, TIMEOUT)
|
||||
tn.write(b"who\n")
|
||||
res=tn.read_until(WAIT_FOR,TIMEOUT)
|
||||
res = tn.read_until(WAIT_FOR, TIMEOUT)
|
||||
tn.write(b"exit\n")
|
||||
|
||||
except EOFError:
|
||||
logging.error ('could not autenticate to telnet dxspider host: check user callsign ')
|
||||
logging.error (res)
|
||||
res=0
|
||||
logging.error(
|
||||
"could not autenticate to telnet dxspider host: check user callsign "
|
||||
)
|
||||
logging.error(res)
|
||||
res = 0
|
||||
except:
|
||||
logging.error ('could not connect to telnet dxspider host: check host/port')
|
||||
ret = ''
|
||||
logging.error("could not connect to telnet dxspider host: check host/port")
|
||||
ret = ""
|
||||
|
||||
if res!=0:
|
||||
ret=parse_who(res)
|
||||
if res != 0:
|
||||
ret = parse_who(res)
|
||||
else:
|
||||
ret=''
|
||||
ret = ""
|
||||
|
||||
return ret
|
||||
|
@ -1,9 +1,9 @@
|
||||
#*************************************************************************************
|
||||
# *************************************************************************************
|
||||
# Module used to convert dxcluster band file to band configuration file modes for
|
||||
# spiderweb
|
||||
# you can use it in build step
|
||||
#*************************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
# *************************************************************************************
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
@ -12,47 +12,53 @@ import json
|
||||
import re
|
||||
import sys
|
||||
|
||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
||||
#dxspider_band='/home/sysop/spider/data/bands.pl'
|
||||
output_modes='../cfg/modes.json'
|
||||
#-------------------------------------------------------------------------------------
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||
datefmt="%m/%d/%Y %I:%M:%S",
|
||||
)
|
||||
# dxspider_band='/home/sysop/spider/data/bands.pl'
|
||||
output_modes = "../cfg/modes.json"
|
||||
# -------------------------------------------------------------------------------------
|
||||
# reads bands file and convert to json
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
def parse(input_file):
|
||||
line_num=0
|
||||
line_num_valid=0
|
||||
data=''
|
||||
line_num = 0
|
||||
line_num_valid = 0
|
||||
data = ""
|
||||
band_trigger = False
|
||||
try:
|
||||
with open(input_file, 'r') as f:
|
||||
with open(input_file, "r") as f:
|
||||
for line in f:
|
||||
line_num+=1
|
||||
li=line.strip()
|
||||
#remove comments
|
||||
line_num += 1
|
||||
li = line.strip()
|
||||
# remove comments
|
||||
if li.startswith("%bands = "):
|
||||
band_trigger = True
|
||||
if li.endswith(");"):
|
||||
band_trigger = False
|
||||
if not li.startswith("#") and band_trigger == True:
|
||||
line_num_valid+=1
|
||||
data+=li
|
||||
line_num_valid += 1
|
||||
data += li
|
||||
logging.debug("first step parsing output: ")
|
||||
logging.debug(data)
|
||||
|
||||
#replacing strings in order to obtain a json
|
||||
data=data.lower()
|
||||
data=data.replace(" ","")
|
||||
data=data.replace("bless","")
|
||||
data=data.replace("%bands=","")
|
||||
data=data.replace(",'bands'","")
|
||||
data=re.sub(r"([\"'])(?:(?=(\\?))\2.)*?\1=>", "", data) #remove token like '136khz' =>
|
||||
data=data.replace("=>",":")
|
||||
data=data.replace("(","")
|
||||
data=data.replace(")","")
|
||||
data=re.sub(r'([a-zA-Z]+):', r'"\1":', data) #add quotation around words
|
||||
data=data.replace(",}","}")
|
||||
data="["+data+"]"
|
||||
data=data.replace(",]","]")
|
||||
# replacing strings in order to obtain a json
|
||||
data = data.lower()
|
||||
data = data.replace(" ", "")
|
||||
data = data.replace("bless", "")
|
||||
data = data.replace("%bands=", "")
|
||||
data = data.replace(",'bands'", "")
|
||||
data = re.sub(
|
||||
r"([\"'])(?:(?=(\\?))\2.)*?\1=>", "", data
|
||||
) # remove token like '136khz' =>
|
||||
data = data.replace("=>", ":")
|
||||
data = data.replace("(", "")
|
||||
data = data.replace(")", "")
|
||||
data = re.sub(r"([a-zA-Z]+):", r'"\1":', data) # add quotation around words
|
||||
data = data.replace(",}", "}")
|
||||
data = "[" + data + "]"
|
||||
data = data.replace(",]", "]")
|
||||
logging.debug("second step parsing output: ")
|
||||
logging.debug(data)
|
||||
|
||||
@ -65,17 +71,20 @@ def parse(input_file):
|
||||
logging.error(input_file)
|
||||
return ""
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# add min max freq element to the related mode in final json
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
def add_freq(mode, freq, json_modes):
|
||||
try:
|
||||
for modes in json_modes['modes']:
|
||||
if modes["id"]==mode:
|
||||
for modes in json_modes["modes"]:
|
||||
if modes["id"] == mode:
|
||||
ind_freq = 0
|
||||
while ind_freq < len(freq):
|
||||
modes["freq"].append({"min":freq[ind_freq],"max":freq[ind_freq+1]})
|
||||
ind_freq +=2
|
||||
modes["freq"].append(
|
||||
{"min": freq[ind_freq], "max": freq[ind_freq + 1]}
|
||||
)
|
||||
ind_freq += 2
|
||||
|
||||
return json_modes
|
||||
|
||||
@ -85,12 +94,15 @@ def add_freq(mode, freq, json_modes):
|
||||
logging.error(message)
|
||||
return {}
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# reads bands file and convert to json
|
||||
#-------------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------------
|
||||
def create_output(data):
|
||||
|
||||
json_modes=json.loads('{"modes":[{"id":"cw","freq":[]},{"id":"phone","freq":[]},{"id":"digi","freq":[]}]}')
|
||||
json_modes = json.loads(
|
||||
'{"modes":[{"id":"cw","freq":[]},{"id":"phone","freq":[]},{"id":"digi","freq":[]}]}'
|
||||
)
|
||||
|
||||
try:
|
||||
for element in data:
|
||||
@ -98,11 +110,11 @@ def create_output(data):
|
||||
if mode == "band":
|
||||
pass
|
||||
elif mode == "cw":
|
||||
json_modes=add_freq("cw",element[mode],json_modes)
|
||||
json_modes = add_freq("cw", element[mode], json_modes)
|
||||
elif mode == "ssb":
|
||||
json_modes=add_freq("phone",element[mode],json_modes)
|
||||
json_modes = add_freq("phone", element[mode], json_modes)
|
||||
else:
|
||||
json_modes=add_freq("digi",element[mode],json_modes)
|
||||
json_modes = add_freq("digi", element[mode], json_modes)
|
||||
|
||||
logging.debug("final step output: ")
|
||||
logging.debug(json.dumps(json_modes))
|
||||
@ -115,23 +127,24 @@ def create_output(data):
|
||||
logging.error(input_file)
|
||||
return {}
|
||||
|
||||
#*************************************************************************************
|
||||
|
||||
# *************************************************************************************
|
||||
# Main
|
||||
#*************************************************************************************
|
||||
logging.info('RDxSpider band file conversion starting...')
|
||||
if len(sys.argv)!=2:
|
||||
# *************************************************************************************
|
||||
logging.info("RDxSpider band file conversion starting...")
|
||||
if len(sys.argv) != 2:
|
||||
logging.error("argument invalid. Specify dxcluster band file")
|
||||
logging.error("use: python get_dxcluster_modes.py input_file")
|
||||
raise Exception()
|
||||
|
||||
dxspider_band=sys.argv[1]
|
||||
dxspider_band = sys.argv[1]
|
||||
|
||||
parsed = parse(dxspider_band)
|
||||
if parsed != "" :
|
||||
json_output=create_output(parsed)
|
||||
with open(output_modes,'w') as outfile:
|
||||
json.dump(json_output, outfile,indent=4)
|
||||
logging.info('modes saved to: '+output_modes)
|
||||
logging.info('DxSpider band file conversion completed')
|
||||
if parsed != "":
|
||||
json_output = create_output(parsed)
|
||||
with open(output_modes, "w") as outfile:
|
||||
json.dump(json_output, outfile, indent=4)
|
||||
logging.info("modes saved to: " + output_modes)
|
||||
logging.info("DxSpider band file conversion completed")
|
||||
else:
|
||||
logging.error("error on parsing input file")
|
||||
|
@ -1,83 +1,101 @@
|
||||
#***********************************************************************************
|
||||
# ***********************************************************************************
|
||||
# Module that contain classess for providing data to plotting front-end page
|
||||
#***********************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
# ***********************************************************************************
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import threading
|
||||
import time
|
||||
from lib.qry import query_manager
|
||||
import pandas as pd
|
||||
import json
|
||||
|
||||
#-----------------------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Base class (as template for other classes)
|
||||
#-----------------------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------------
|
||||
class BaseDataProvider:
|
||||
|
||||
#glb_data is used for store and return informations to within get_data method
|
||||
# glb_data is used for store and return informations to within get_data method
|
||||
global glb_data
|
||||
global glb_last_refresh
|
||||
global glb_response
|
||||
|
||||
#refresh is used to refetch data from db and store in memory. you can define a
|
||||
# refresh is used to refetch data from db and store in memory. you can define a
|
||||
# time for refresh
|
||||
def refresh(self):
|
||||
self.logger.info("Class: %s refresh data",self.__class__.__name__)
|
||||
self.logger.info("Class: %s refresh data", self.__class__.__name__)
|
||||
return {}
|
||||
|
||||
#return data to the caller
|
||||
# return data to the caller
|
||||
def get_data(self):
|
||||
self.glb_response = {}
|
||||
self.glb_response.update({"last_refresh":self.glb_last_refresh})
|
||||
self.glb_response.update({"last_refresh": self.glb_last_refresh})
|
||||
return self.glb_response
|
||||
|
||||
#constructor: you have to pass logger, continent list and bands (for frequencies)
|
||||
# constructor: you have to pass logger, continent list and bands (for frequencies)
|
||||
# this method call the first refresh
|
||||
def __init__(self, logger, qm, continents, bands):
|
||||
self.logger = logger
|
||||
self.logger.info("Class: %s init start",self.__class__.__name__)
|
||||
self.logger.info("Class: %s init start", self.__class__.__name__)
|
||||
self.qm = qm
|
||||
self.continents=continents
|
||||
self.bands=bands
|
||||
self.continents = continents
|
||||
self.bands = bands
|
||||
self.refresh()
|
||||
self.logger.info("Class: %s init end",self.__class__.__name__)
|
||||
self.logger.info("Class: %s init end", self.__class__.__name__)
|
||||
return
|
||||
|
||||
#-----------------------------------------------------------------------------------
|
||||
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Class for managing data for Continent/Band chart
|
||||
#-----------------------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------------
|
||||
class ContinentsBandsProvider(BaseDataProvider):
|
||||
|
||||
def __init__(self,logger, qm, continents, bands):
|
||||
def __init__(self, logger, qm, continents, bands):
|
||||
# Calling constructor of base class
|
||||
super().__init__(logger,qm,continents,bands)
|
||||
super().__init__(logger, qm, continents, bands)
|
||||
|
||||
def __load_data(self,band_frequencies, continents_cq):
|
||||
def __load_data(self, band_frequencies, continents_cq):
|
||||
|
||||
self.logger.info("Start")
|
||||
self.logger.info("doing query...")
|
||||
|
||||
#construct bands query
|
||||
bands_qry_string = 'CASE '
|
||||
# construct bands query
|
||||
bands_qry_string = "CASE "
|
||||
self.logger.debug(band_frequencies)
|
||||
for i in range(len(band_frequencies["bands"])):
|
||||
bands_qry_string+=' WHEN freq between '+str(band_frequencies["bands"][i]["min"])+' AND '+ str(band_frequencies["bands"][i]["max"])
|
||||
bands_qry_string+=' THEN "'+band_frequencies["bands"][i]["id"]+'"'
|
||||
bands_qry_string += (
|
||||
" WHEN freq between "
|
||||
+ str(band_frequencies["bands"][i]["min"])
|
||||
+ " AND "
|
||||
+ str(band_frequencies["bands"][i]["max"])
|
||||
)
|
||||
bands_qry_string += ' THEN "' + band_frequencies["bands"][i]["id"] + '"'
|
||||
|
||||
#construct continent region query
|
||||
spottercq_qry_string = 'CASE '
|
||||
spotcq_qry_string = 'CASE '
|
||||
# construct continent region query
|
||||
spottercq_qry_string = "CASE "
|
||||
spotcq_qry_string = "CASE "
|
||||
for i in range(len(continents_cq["continents"])):
|
||||
spottercq_qry_string+=' WHEN spottercq in('+continents_cq["continents"][i]["cq"]+')'
|
||||
spottercq_qry_string+=' THEN "' +continents_cq["continents"][i]["id"]+'"'
|
||||
spotcq_qry_string+=' WHEN spotcq in('+continents_cq["continents"][i]["cq"]+')'
|
||||
spotcq_qry_string+=' THEN "' +continents_cq["continents"][i]["id"]+'"'
|
||||
spottercq_qry_string += (
|
||||
" WHEN spottercq in(" + continents_cq["continents"][i]["cq"] + ")"
|
||||
)
|
||||
spottercq_qry_string += (
|
||||
' THEN "' + continents_cq["continents"][i]["id"] + '"'
|
||||
)
|
||||
spotcq_qry_string += (
|
||||
" WHEN spotcq in(" + continents_cq["continents"][i]["cq"] + ")"
|
||||
)
|
||||
spotcq_qry_string += ' THEN "' + continents_cq["continents"][i]["id"] + '"'
|
||||
|
||||
#construct final query string
|
||||
qry_string ="""
|
||||
# construct final query string
|
||||
qry_string = (
|
||||
"""
|
||||
SELECT
|
||||
"""+spottercq_qry_string+""" ELSE spottercq END,
|
||||
"""+spotcq_qry_string+""" ELSE spotcq END,
|
||||
"""+bands_qry_string+""" END,
|
||||
"""
|
||||
+ spottercq_qry_string
|
||||
+ """ ELSE spottercq END,
|
||||
"""
|
||||
+ spotcq_qry_string
|
||||
+ """ ELSE spotcq END,
|
||||
"""
|
||||
+ bands_qry_string
|
||||
+ """ END,
|
||||
count(0) number
|
||||
from spot
|
||||
where
|
||||
@ -86,25 +104,26 @@ class ContinentsBandsProvider(BaseDataProvider):
|
||||
group by 1, 2, 3
|
||||
;
|
||||
"""
|
||||
)
|
||||
|
||||
self.logger.debug(qry_string)
|
||||
self.qm.qry(qry_string)
|
||||
data=self.qm.get_data()
|
||||
if len(data)==0:
|
||||
data = self.qm.get_data()
|
||||
if len(data) == 0:
|
||||
self.logger.warning("no data found")
|
||||
|
||||
self.logger.info("query done")
|
||||
self.logger.debug (data)
|
||||
self.logger.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
# function for search continent in the global data returned by query and making a cartesian product
|
||||
# in order to prepare data for heatmap
|
||||
def __normalize_continent(self,data_list,continent,continents_list, band_list):
|
||||
data_filtered=[]
|
||||
def __normalize_continent(self, data_list, continent, continents_list, band_list):
|
||||
data_filtered = []
|
||||
for i, item_data in enumerate(data_list):
|
||||
if item_data[0]==continent and not (item_data[3] is None):
|
||||
element=[]
|
||||
if item_data[0] == continent and not (item_data[3] is None):
|
||||
element = []
|
||||
element.append(item_data[1])
|
||||
element.append(item_data[2])
|
||||
element.append(item_data[3])
|
||||
@ -114,63 +133,68 @@ class ContinentsBandsProvider(BaseDataProvider):
|
||||
|
||||
for j, item_continent in enumerate(continents_list):
|
||||
for k, item_band in enumerate(band_list):
|
||||
found=0
|
||||
found = 0
|
||||
for lis, item_filtered in enumerate(data_filtered):
|
||||
if item_filtered[0]==item_continent["id"] and item_filtered[1]==item_band["id"]:
|
||||
#cartesian_product.append(item_filtered)
|
||||
element=[]
|
||||
if (
|
||||
item_filtered[0] == item_continent["id"]
|
||||
and item_filtered[1] == item_band["id"]
|
||||
):
|
||||
# cartesian_product.append(item_filtered)
|
||||
element = []
|
||||
element.append(j)
|
||||
element.append(k)
|
||||
element.append(item_filtered[2])
|
||||
cartesian_product.append(element)
|
||||
found=1
|
||||
if found==0:
|
||||
element=[]
|
||||
found = 1
|
||||
if found == 0:
|
||||
element = []
|
||||
element.append(j)
|
||||
element.append(k)
|
||||
element.append(0)
|
||||
cartesian_product.append(element)
|
||||
|
||||
self.logger.debug("cartesian product for continent: "+continent)
|
||||
self.logger.debug("cartesian product for continent: " + continent)
|
||||
self.logger.debug(cartesian_product)
|
||||
return cartesian_product
|
||||
|
||||
def refresh(self):
|
||||
super().refresh()
|
||||
lcl_data={}
|
||||
qry_data=self.__load_data(self.bands, self.continents)
|
||||
lcl_data = {}
|
||||
qry_data = self.__load_data(self.bands, self.continents)
|
||||
for i, item in enumerate(self.continents["continents"]):
|
||||
continent=item["id"]
|
||||
data_de=self.__normalize_continent(qry_data,continent,self.continents["continents"],self.bands["bands"])
|
||||
lcl_data.update({continent:data_de})
|
||||
continent = item["id"]
|
||||
data_de = self.__normalize_continent(
|
||||
qry_data, continent, self.continents["continents"], self.bands["bands"]
|
||||
)
|
||||
lcl_data.update({continent: data_de})
|
||||
|
||||
self.glb_data=lcl_data
|
||||
self.glb_last_refresh=time.time()
|
||||
self.glb_data = lcl_data
|
||||
self.glb_last_refresh = time.time()
|
||||
|
||||
threading.Timer(15*60,self.refresh).start() #periodic refresh: set time
|
||||
threading.Timer(15 * 60, self.refresh).start() # periodic refresh: set time
|
||||
return
|
||||
|
||||
def get_data(self,continent_filter):
|
||||
def get_data(self, continent_filter):
|
||||
super().get_data()
|
||||
self.glb_response.update({"band activity": self.glb_data[continent_filter]})
|
||||
return self.glb_response
|
||||
|
||||
#-----------------------------------------------------------------------------------
|
||||
# Class for managing data for Spots per months chart
|
||||
#-----------------------------------------------------------------------------------
|
||||
class SpotsPerMounthProvider(BaseDataProvider):
|
||||
|
||||
def __init__(self,logger,qm):
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Class for managing data for Spots per months chart
|
||||
# -----------------------------------------------------------------------------------
|
||||
class SpotsPerMounthProvider(BaseDataProvider):
|
||||
def __init__(self, logger, qm):
|
||||
# Calling constructor of base class
|
||||
super().__init__(logger,qm,[],[])
|
||||
super().__init__(logger, qm, [], [])
|
||||
|
||||
def __load_data(self):
|
||||
|
||||
self.logger.info("Start")
|
||||
self.logger.info("doing query...")
|
||||
|
||||
#construct final query string
|
||||
qry_string="""
|
||||
# construct final query string
|
||||
qry_string = """
|
||||
select month(s1.ym) as referring_month,
|
||||
cast(sum(
|
||||
case
|
||||
@ -228,53 +252,57 @@ class SpotsPerMounthProvider(BaseDataProvider):
|
||||
"""
|
||||
self.logger.debug(qry_string)
|
||||
self.qm.qry(qry_string)
|
||||
data=self.qm.get_data()
|
||||
if len(data)==0:
|
||||
data = self.qm.get_data()
|
||||
if len(data) == 0:
|
||||
self.logger.warning("no data found")
|
||||
|
||||
self.logger.info("query done")
|
||||
self.logger.debug (data)
|
||||
self.logger.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def refresh(self):
|
||||
super().refresh()
|
||||
lcl_data={}
|
||||
qry_data=self.__load_data()
|
||||
lcl_data = {}
|
||||
qry_data = self.__load_data()
|
||||
|
||||
for i, item in enumerate(qry_data):
|
||||
year_data={'year_0':item[1],'year_1':item[2],'year_2':item[3]}
|
||||
lcl_data.update({item[0]:year_data})
|
||||
year_data = {"year_0": item[1], "year_1": item[2], "year_2": item[3]}
|
||||
lcl_data.update({item[0]: year_data})
|
||||
|
||||
self.logger.debug(lcl_data)
|
||||
|
||||
self.glb_data=lcl_data
|
||||
self.glb_last_refresh=time.time()
|
||||
self.glb_data = lcl_data
|
||||
self.glb_last_refresh = time.time()
|
||||
|
||||
threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time
|
||||
threading.Timer(
|
||||
60 * 60 * 24, self.refresh
|
||||
).start() # periodic refresh: set time
|
||||
return
|
||||
|
||||
def get_data(self,):
|
||||
def get_data(
|
||||
self,
|
||||
):
|
||||
super().get_data()
|
||||
self.glb_response.update({"spots_per_month": self.glb_data})
|
||||
return self.glb_response
|
||||
|
||||
#-----------------------------------------------------------------------------------
|
||||
# Class for managing data for Spots trend chart
|
||||
#-----------------------------------------------------------------------------------
|
||||
class SpotsTrend(BaseDataProvider):
|
||||
|
||||
def __init__(self,logger,qm):
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Class for managing data for Spots trend chart
|
||||
# -----------------------------------------------------------------------------------
|
||||
class SpotsTrend(BaseDataProvider):
|
||||
def __init__(self, logger, qm):
|
||||
# Calling constructor of base class
|
||||
super().__init__(logger,qm,[],[])
|
||||
super().__init__(logger, qm, [], [])
|
||||
|
||||
def __load_data(self):
|
||||
|
||||
self.logger.info("Start")
|
||||
self.logger.info("doing query...")
|
||||
|
||||
#construct final query string
|
||||
qry_string="""
|
||||
# construct final query string
|
||||
qry_string = """
|
||||
select
|
||||
FROM_UNIXTIME(time,'%Y-%m-%d') as day,
|
||||
count(0) as total
|
||||
@ -285,39 +313,43 @@ class SpotsTrend(BaseDataProvider):
|
||||
"""
|
||||
self.logger.debug(qry_string)
|
||||
self.qm.qry_pd(qry_string)
|
||||
df=self.qm.get_data()
|
||||
df = self.qm.get_data()
|
||||
|
||||
self.logger.info("query done")
|
||||
self.logger.debug (df)
|
||||
self.logger.debug(df)
|
||||
|
||||
if len(df)==0:
|
||||
if len(df) == 0:
|
||||
self.logger.warning("no data found")
|
||||
|
||||
#normalize data eliminating peaks
|
||||
df['day']=pd.to_datetime(df['day'])
|
||||
df=df.set_index('day')
|
||||
df=df.resample('D').interpolate(method='pad', limit_direction='forward', axis=0)
|
||||
df=df.rolling('30D').mean()
|
||||
df['total']=df['total'].round(0)
|
||||
# normalize data eliminating peaks
|
||||
df["day"] = pd.to_datetime(df["day"])
|
||||
df = df.set_index("day")
|
||||
df = df.resample("D").interpolate(
|
||||
method="pad", limit_direction="forward", axis=0
|
||||
)
|
||||
df = df.rolling("30D").mean()
|
||||
df["total"] = df["total"].round(0)
|
||||
|
||||
return df
|
||||
|
||||
def refresh(self):
|
||||
super().refresh()
|
||||
qry_data=self.__load_data()
|
||||
qry_data = self.__load_data()
|
||||
|
||||
lcl_data={}
|
||||
lcl_data = {}
|
||||
|
||||
#iterate panda dataframe
|
||||
# iterate panda dataframe
|
||||
for index, row in qry_data.iterrows():
|
||||
lcl_data.update({str(index.date()):row['total']})
|
||||
lcl_data.update({str(index.date()): row["total"]})
|
||||
|
||||
self.logger.debug(lcl_data)
|
||||
|
||||
self.glb_data=lcl_data
|
||||
self.glb_last_refresh=time.time()
|
||||
self.glb_data = lcl_data
|
||||
self.glb_last_refresh = time.time()
|
||||
|
||||
threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time
|
||||
threading.Timer(
|
||||
60 * 60 * 24, self.refresh
|
||||
).start() # periodic refresh: set time
|
||||
return
|
||||
|
||||
def get_data(self):
|
||||
@ -325,14 +357,14 @@ class SpotsTrend(BaseDataProvider):
|
||||
self.glb_response.update({"spots_trend": self.glb_data})
|
||||
return self.glb_response
|
||||
|
||||
#-----------------------------------------------------------------------------------
|
||||
# Class for managing data for Hour/Band chart
|
||||
#-----------------------------------------------------------------------------------
|
||||
class HourBand(BaseDataProvider):
|
||||
|
||||
def __init__(self,logger,qm,bands):
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Class for managing data for Hour/Band chart
|
||||
# -----------------------------------------------------------------------------------
|
||||
class HourBand(BaseDataProvider):
|
||||
def __init__(self, logger, qm, bands):
|
||||
# Calling constructor of base class
|
||||
super().__init__(logger,qm,[],bands)
|
||||
super().__init__(logger, qm, [], bands)
|
||||
|
||||
def __load_data(self):
|
||||
|
||||
@ -340,25 +372,42 @@ class HourBand(BaseDataProvider):
|
||||
self.logger.info("doing query...")
|
||||
|
||||
self.logger.debug(self.bands)
|
||||
#construct bands query
|
||||
bands_qry_string = 'CASE '
|
||||
# construct bands query
|
||||
bands_qry_string = "CASE "
|
||||
for i in range(len(self.bands["bands"])):
|
||||
bands_qry_string+=' WHEN freq between '+str(self.bands["bands"][i]["min"])+' AND '+ str(self.bands["bands"][i]["max"])
|
||||
bands_qry_string+=' THEN "'+self.bands["bands"][i]["id"]+'"'
|
||||
bands_qry_string += (
|
||||
" WHEN freq between "
|
||||
+ str(self.bands["bands"][i]["min"])
|
||||
+ " AND "
|
||||
+ str(self.bands["bands"][i]["max"])
|
||||
)
|
||||
bands_qry_string += ' THEN "' + self.bands["bands"][i]["id"] + '"'
|
||||
|
||||
#construct bands query weight
|
||||
bands_weight_qry_string = 'CASE '
|
||||
# construct bands query weight
|
||||
bands_weight_qry_string = "CASE "
|
||||
for i in range(len(self.bands["bands"])):
|
||||
bands_weight_qry_string+=' WHEN freq between '+str(self.bands["bands"][i]["min"])+' AND '+ str(self.bands["bands"][i]["max"])
|
||||
bands_weight_qry_string+=' THEN "'+str(self.bands["bands"][i]["min"])+'"'
|
||||
bands_weight_qry_string += (
|
||||
" WHEN freq between "
|
||||
+ str(self.bands["bands"][i]["min"])
|
||||
+ " AND "
|
||||
+ str(self.bands["bands"][i]["max"])
|
||||
)
|
||||
bands_weight_qry_string += (
|
||||
' THEN "' + str(self.bands["bands"][i]["min"]) + '"'
|
||||
)
|
||||
|
||||
#construct final query string
|
||||
qry_string ="""
|
||||
# construct final query string
|
||||
qry_string = (
|
||||
"""
|
||||
select s1.band, s1.hour, s1.total from (
|
||||
SELECT
|
||||
cast(concat(HOUR (FROM_UNIXTIME(time))) as unsigned) as hour,
|
||||
"""+bands_qry_string+""" ELSE "other" END as band,
|
||||
cast("""+bands_weight_qry_string+""" ELSE 0 END as unsigned) as band_weight,
|
||||
"""
|
||||
+ bands_qry_string
|
||||
+ """ ELSE "other" END as band,
|
||||
cast("""
|
||||
+ bands_weight_qry_string
|
||||
+ """ ELSE 0 END as unsigned) as band_weight,
|
||||
count(0) AS total
|
||||
from spot
|
||||
WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL 1 MONTH)
|
||||
@ -368,34 +417,37 @@ class HourBand(BaseDataProvider):
|
||||
order by s1.band, s1.hour
|
||||
;
|
||||
"""
|
||||
)
|
||||
|
||||
self.logger.debug(qry_string)
|
||||
self.qm.qry(qry_string)
|
||||
data=self.qm.get_data()
|
||||
if len(data)==0:
|
||||
data = self.qm.get_data()
|
||||
if len(data) == 0:
|
||||
self.logger.warning("no data found")
|
||||
|
||||
self.logger.info("query done")
|
||||
self.logger.debug (data)
|
||||
self.logger.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def refresh(self):
|
||||
super().refresh()
|
||||
lcl_data={}
|
||||
qry_data=self.__load_data()
|
||||
lcl_data = {}
|
||||
qry_data = self.__load_data()
|
||||
|
||||
for i,j,k in qry_data:
|
||||
for i, j, k in qry_data:
|
||||
if i not in lcl_data:
|
||||
lcl_data[i]={}
|
||||
lcl_data[i].update({j:k})
|
||||
lcl_data[i] = {}
|
||||
lcl_data[i].update({j: k})
|
||||
|
||||
self.logger.debug(lcl_data)
|
||||
|
||||
self.glb_data=lcl_data
|
||||
self.glb_last_refresh=time.time()
|
||||
self.glb_data = lcl_data
|
||||
self.glb_last_refresh = time.time()
|
||||
|
||||
threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time
|
||||
threading.Timer(
|
||||
60 * 60 * 24, self.refresh
|
||||
).start() # periodic refresh: set time
|
||||
return
|
||||
|
||||
def get_data(self):
|
||||
@ -404,24 +456,24 @@ class HourBand(BaseDataProvider):
|
||||
return self.glb_response
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Class for managing data for World DX SPOTS current activity
|
||||
#-----------------------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------------
|
||||
class WorldDxSpotsLive(BaseDataProvider):
|
||||
|
||||
global glb_pfxt
|
||||
|
||||
def __init__(self,logger,qm,pfxt):
|
||||
def __init__(self, logger, qm, pfxt):
|
||||
# Calling constructor of base class
|
||||
self.glb_pfxt=pfxt
|
||||
super().__init__(logger,qm,[],[])
|
||||
self.glb_pfxt = pfxt
|
||||
super().__init__(logger, qm, [], [])
|
||||
|
||||
def __load_data(self):
|
||||
self.logger.info("Start")
|
||||
self.logger.info("doing query...")
|
||||
|
||||
#construct final query string
|
||||
qry_string ="""
|
||||
# construct final query string
|
||||
qry_string = """
|
||||
select spotcall as dx
|
||||
from spot
|
||||
WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL 1 HOUR)
|
||||
@ -431,63 +483,63 @@ class WorldDxSpotsLive(BaseDataProvider):
|
||||
|
||||
self.logger.debug(qry_string)
|
||||
self.qm.qry(qry_string)
|
||||
data=self.qm.get_data()
|
||||
row_headers=self.qm.get_headers()
|
||||
if len(data)==0:
|
||||
data = self.qm.get_data()
|
||||
row_headers = self.qm.get_headers()
|
||||
if len(data) == 0:
|
||||
self.logger.warning("no data found")
|
||||
|
||||
self.logger.info("query done")
|
||||
self.logger.debug (data)
|
||||
self.logger.debug(data)
|
||||
|
||||
#define country table for search info on callsigns
|
||||
df = pd.DataFrame(columns=['row_id','dx','lat','lon'])
|
||||
dx=[]
|
||||
lat=[]
|
||||
lon=[]
|
||||
row_id=[]
|
||||
idx=0
|
||||
# define country table for search info on callsigns
|
||||
df = pd.DataFrame(columns=["row_id", "dx", "lat", "lon"])
|
||||
dx = []
|
||||
lat = []
|
||||
lon = []
|
||||
row_id = []
|
||||
idx = 0
|
||||
|
||||
for result in data:
|
||||
main_result=dict(zip(row_headers,result))
|
||||
main_result = dict(zip(row_headers, result))
|
||||
# find the country in prefix table
|
||||
search_prefix=self.glb_pfxt.find(main_result["dx"])
|
||||
if search_prefix["country"] != "unknown country" :
|
||||
search_prefix = self.glb_pfxt.find(main_result["dx"])
|
||||
if search_prefix["country"] != "unknown country":
|
||||
# merge recordset and contry prefix
|
||||
dx.append(main_result["dx"])
|
||||
lon.append(float(search_prefix["lat"]))
|
||||
lat.append(-float(search_prefix["lon"]))
|
||||
idx+=1
|
||||
idx += 1
|
||||
row_id.append(idx)
|
||||
|
||||
df['dx']=dx
|
||||
df['lat']=lat
|
||||
df['lon']=lon
|
||||
df['row_id']=row_id
|
||||
df_grp=df.groupby(["lat", "lon"])["row_id"].count().reset_index(name="count")
|
||||
df["dx"] = dx
|
||||
df["lat"] = lat
|
||||
df["lon"] = lon
|
||||
df["row_id"] = row_id
|
||||
df_grp = df.groupby(["lat", "lon"])["row_id"].count().reset_index(name="count")
|
||||
|
||||
if df is None ==0:
|
||||
if df is None == 0:
|
||||
logger.warning("no data found")
|
||||
|
||||
return df_grp
|
||||
|
||||
def refresh(self):
|
||||
super().refresh()
|
||||
lcl_data={}
|
||||
qry_data=self.__load_data()
|
||||
lcl_data = {}
|
||||
qry_data = self.__load_data()
|
||||
|
||||
self.logger.debug(qry_data)
|
||||
|
||||
lcl_data=[]
|
||||
lcl_data = []
|
||||
for index, row in qry_data.iterrows():
|
||||
record=dict(lat=row['lat'], lon=row['lon'], count=row['count'])
|
||||
record = dict(lat=row["lat"], lon=row["lon"], count=row["count"])
|
||||
lcl_data.append(record)
|
||||
|
||||
self.logger.debug(lcl_data)
|
||||
|
||||
self.glb_data=lcl_data
|
||||
self.glb_last_refresh=time.time()
|
||||
self.glb_data = lcl_data
|
||||
self.glb_last_refresh = time.time()
|
||||
|
||||
threading.Timer(5*60,self.refresh).start() #periodic refresh: set time
|
||||
threading.Timer(5 * 60, self.refresh).start() # periodic refresh: set time
|
||||
return
|
||||
|
||||
def get_data(self):
|
||||
|
66
lib/qry.py
66
lib/qry.py
@ -1,55 +1,63 @@
|
||||
#*****************************************************************************************
|
||||
# *****************************************************************************************
|
||||
# module used to make query to mysql
|
||||
# TODO: manage polymorfism and use only one qry sign
|
||||
#*****************************************************************************************
|
||||
#import MySQLdb as my
|
||||
# *****************************************************************************************
|
||||
# import MySQLdb as my
|
||||
import mysql.connector as my
|
||||
from mysql.connector import pooling
|
||||
import logging
|
||||
import json
|
||||
import pandas as pd
|
||||
|
||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||
datefmt="%m/%d/%Y %I:%M:%S",
|
||||
)
|
||||
|
||||
|
||||
class query_manager:
|
||||
#connection definition
|
||||
# connection definition
|
||||
|
||||
def __init__(self):
|
||||
try:
|
||||
with open('../cfg/config.json') as json_data_file:
|
||||
with open("../cfg/config.json") as json_data_file:
|
||||
cfg = json.load(json_data_file)
|
||||
except Exception as e1:
|
||||
logging.info(e1)
|
||||
logging.info('trying with other path...')
|
||||
logging.info("trying with other path...")
|
||||
try:
|
||||
with open ('cfg/config.json') as json_data_file:
|
||||
with open("cfg/config.json") as json_data_file:
|
||||
cfg = json.load(json_data_file)
|
||||
except Exception as e2:
|
||||
logging.error(e2)
|
||||
return
|
||||
|
||||
logging.info('config file loaded')
|
||||
self.__cnxpool = pooling.MySQLConnectionPool(host=cfg['mysql']['host'],
|
||||
user=cfg['mysql']['user'],
|
||||
passwd=cfg['mysql']['passwd'],
|
||||
db=cfg['mysql']['db'],
|
||||
charset='latin1',
|
||||
# charset='utf8mb4',
|
||||
# collation = 'utf8mb4_general_ci',
|
||||
pool_name = "spider_pool",
|
||||
use_pure = True,
|
||||
pool_size = 3
|
||||
logging.info("config file loaded")
|
||||
self.__cnxpool = pooling.MySQLConnectionPool(
|
||||
host=cfg["mysql"]["host"],
|
||||
user=cfg["mysql"]["user"],
|
||||
passwd=cfg["mysql"]["passwd"],
|
||||
db=cfg["mysql"]["db"],
|
||||
charset="latin1",
|
||||
# charset='utf8mb4',
|
||||
# collation = 'utf8mb4_general_ci',
|
||||
pool_name="spider_pool",
|
||||
use_pure=True,
|
||||
pool_size=3,
|
||||
)
|
||||
logging.info('db connection pool created')
|
||||
logging.info("db connection pool created")
|
||||
|
||||
#normal query
|
||||
def qry(self,qs,prepared_statement=False):
|
||||
# normal query
|
||||
def qry(self, qs, prepared_statement=False):
|
||||
try:
|
||||
cnx=self.__cnxpool.get_connection()
|
||||
cnx = self.__cnxpool.get_connection()
|
||||
cursor = cnx.cursor(prepared=prepared_statement)
|
||||
cursor.execute(qs)
|
||||
self.__data=cursor.fetchall()
|
||||
self.__row_headers=[x[0] for x in cursor.description] #this will extract row headers
|
||||
self.__data = cursor.fetchall()
|
||||
self.__row_headers = [
|
||||
x[0] for x in cursor.description
|
||||
] # this will extract row headers
|
||||
cursor.close()
|
||||
except Exception as e2:
|
||||
logging.error(e2)
|
||||
@ -62,11 +70,11 @@ class query_manager:
|
||||
def get_headers(self):
|
||||
return self.__row_headers
|
||||
|
||||
#query with pandas
|
||||
def qry_pd(self,qs):
|
||||
# query with pandas
|
||||
def qry_pd(self, qs):
|
||||
try:
|
||||
cnx=self.__cnxpool.get_connection()
|
||||
self.__data = pd.read_sql(qs,con=cnx)
|
||||
cnx = self.__cnxpool.get_connection()
|
||||
self.__data = pd.read_sql(qs, con=cnx)
|
||||
except Exception as e2:
|
||||
logging.error(e2)
|
||||
finally:
|
||||
|
@ -1,15 +1,20 @@
|
||||
#
|
||||
# little script used to build static pages
|
||||
#
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
from staticjinja import Site
|
||||
|
||||
|
||||
def cookies_check():
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
site = Site.make_site(searchpath="../static/html/templates/",outpath="../static/html/",env_globals={
|
||||
'cookies_check':cookies_check,
|
||||
})
|
||||
site = Site.make_site(
|
||||
searchpath="../static/html/dev/",
|
||||
outpath="../static/html/rel/",
|
||||
env_globals={
|
||||
"cookies_check": cookies_check,
|
||||
},
|
||||
)
|
||||
site.render(use_reloader=False)
|
||||
|
@ -1 +1 @@
|
||||
nohup ./test.sh >/dev/null 2>&1 &
|
||||
nohup ./test.sh -r >/dev/null 2>&1 &
|
||||
|
@ -1,5 +1,7 @@
|
||||
astroid==2.12.14
|
||||
charset-normalizer==2.1.1
|
||||
click==8.1.3
|
||||
dill==0.3.6
|
||||
docopt-ng==0.8.1
|
||||
easywatch==0.0.5
|
||||
Flask==2.2.2
|
||||
@ -7,14 +9,18 @@ Flask-Minify==0.41
|
||||
Flask-WTF==1.0.1
|
||||
htmlmin==0.1.12
|
||||
idna==3.4
|
||||
isort==5.11.4
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.2
|
||||
jsmin==3.0.1
|
||||
lazy-object-proxy==1.9.0
|
||||
lesscpy==0.15.1
|
||||
MarkupSafe==2.1.1
|
||||
mccabe==0.7.0
|
||||
mysql-connector-python==8.0.31
|
||||
numpy==1.24.1
|
||||
pandas==1.5.2
|
||||
platformdirs==2.6.2
|
||||
ply==3.11
|
||||
protobuf==4.21.12
|
||||
python-dateutil==2.8.2
|
||||
@ -22,8 +28,10 @@ pytz==2022.7
|
||||
rcssmin==1.1.1
|
||||
requests==2.28.1
|
||||
six==1.16.0
|
||||
tomlkit==0.11.6
|
||||
urllib3==1.26.13
|
||||
watchdog==2.2.0
|
||||
Werkzeug==2.2.2
|
||||
wrapt==1.14.1
|
||||
WTForms==3.0.1
|
||||
xxhash==3.1.0
|
||||
|
185
scripts/build.sh
185
scripts/build.sh
@ -8,13 +8,141 @@ path_static='../static'
|
||||
path_static_html=${path_static}'/html'
|
||||
path_static_js=${path_static}'/js'
|
||||
path_static_css=${path_static}'/css'
|
||||
path_cfg='../cfg'
|
||||
app_ini=${path_cfg}'/webapp_log_config.ini'
|
||||
path_docs='../docs'
|
||||
readme='../README.md'
|
||||
manifest=${path_static}'/manifest.webmanifest'
|
||||
changelog=${path_docs}'/'CHANGELOG.md
|
||||
|
||||
#echo '*** SPIDERWEB building process ***'
|
||||
html_change_references(){
|
||||
for i in ${path_templates}/*.html
|
||||
do
|
||||
echo "changing references to scripts in ${i}"
|
||||
if [ "${1}" == "-r" ]; then
|
||||
# concatenating 2 sed command with ";"
|
||||
# 1) if found static/js/dev/ replace .js with .min.js
|
||||
# 2) replace static/js/dev/ with static/js/rel/
|
||||
if ! sed -i '/static\/js\/dev/s/\.js/\.min\.js/;s/static\/js\/dev/static\/js\/rel/g' ${i}; then
|
||||
echo 'ERROR replacing .js to .min.js '
|
||||
exit 6
|
||||
fi
|
||||
# the same but for css
|
||||
if ! sed -i '/static\/css\/dev/s/\.css/\.min\.css/;s/static\/css\/dev/static\/css\/rel/g' ${i}; then
|
||||
echo 'ERROR replacing .css to .min.css '
|
||||
exit 6
|
||||
fi
|
||||
elif [ "${1}" == "-d" ]; then
|
||||
# concatenating 2 sed command with ";"
|
||||
# 1) if found static/js/rel/ replace .min.js with .js
|
||||
# 2) replace static/js/rel/ with static/js/dev/
|
||||
if ! sed -i '/static\/js\/rel/s/\.min\.js/\.js/;s/static\/js\/rel/static\/js\/dev/g' ${i}; then
|
||||
echo 'ERROR replacing .min.js to .js'
|
||||
exit 6
|
||||
fi
|
||||
# the same but for css
|
||||
if ! sed -i '/static\/css\/rel/s/\.min\.css/\.css/;s/static\/css\/rel/static\/css\/dev/g' ${i}; then
|
||||
echo 'ERROR replacing .min.css to .css'
|
||||
exit 6
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
if [ "$1" != "-r" ] && [ "$1" != "-d" ]; then
|
||||
echo 'wrong options:'
|
||||
echo ' -d: debug'
|
||||
echo ' -r: release'
|
||||
exit 5
|
||||
fi
|
||||
|
||||
echo '*** SPIDERWEB building process ***'
|
||||
if [ "$1" == "-r" ]; then
|
||||
echo 'creating RELEASE application'
|
||||
|
||||
#used to minify the application javascript
|
||||
echo 'minify javascripts...'
|
||||
shopt -s extglob
|
||||
rm ${path_static_js}/rel/*.js
|
||||
for i in ${path_static_js}/dev/*.js
|
||||
do
|
||||
[[ -e ${i} ]] || break # handle the case of no files found
|
||||
file_no_ext=$(basename "${i%.js}")
|
||||
out=${path_static_js}/rel/${file_no_ext}.min.js
|
||||
echo "${i} --> ${out}"
|
||||
if ! uglifyjs --compress --mangle -- ${i} > ${out}
|
||||
then
|
||||
echo 'ERROR minifying javascript: '${i}
|
||||
shopt -u extglob
|
||||
exit 80
|
||||
fi
|
||||
if [ ! -s "${out}" ]; then
|
||||
echo "File is empty"
|
||||
shopt -u extglob
|
||||
exit 81
|
||||
fi
|
||||
done
|
||||
|
||||
#used to minify css
|
||||
echo 'minify css...'
|
||||
rm ${path_static_css}/rel/*.css
|
||||
for i in ${path_static_css}/dev/*.css
|
||||
do
|
||||
[[ -e ${i} ]] || break # handle the case of no files found
|
||||
echo ${i}
|
||||
file_no_ext=$(basename "${i%.css}")
|
||||
out=${path_static_css}/rel/${file_no_ext}.min.css
|
||||
echo "${i} --> ${out}"
|
||||
#if ! curl -X POST -s --fail --compressed --data "code_type=css" --data-urlencode 'code@'${i} https://htmlcompressor.com/compress > ${path_static_css}/rel/${file_no_ext}.min.css
|
||||
if ! css-minify -f ${i} -o ${path_static_css}/rel/
|
||||
then
|
||||
echo 'ERROR minifying css: ' ${out}
|
||||
shopt -u extglob
|
||||
exit 90
|
||||
fi
|
||||
if [ ! -s "${out}" ]; then
|
||||
echo "File is empty"
|
||||
shopt -u extglob
|
||||
exit 91
|
||||
fi
|
||||
#sleep 5
|
||||
done
|
||||
shopt -u extglob
|
||||
html_change_references -r
|
||||
|
||||
echo 'writing requirements...'
|
||||
if ! pip freeze|tee ../requirements.txt
|
||||
then
|
||||
echo 'ERROR wrinting requirements'
|
||||
exit 60
|
||||
fi
|
||||
|
||||
echo 'remove some packages from requirements...'
|
||||
sed -i '/certifi==/d' ../requirements.txt
|
||||
sed -i '/staticjinja==/d' ../requirements.txt
|
||||
|
||||
if ! sed -i 's/level=DEBUG/level=INFO/g' ${app_ini}; then
|
||||
echo 'ERROR settimg loglevel=INFO '
|
||||
exit 12
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [ "$1" == "-d" ]; then
|
||||
echo 'creating DEBUG application'
|
||||
html_change_references -d
|
||||
|
||||
if ! sed -i 's/level=INFO/level=DEBUG/g' ${app_ini}; then
|
||||
echo 'ERROR settimg loglevel=DEBUG '
|
||||
exit 12
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
|
||||
#echo 'create modes.json from dxspider bands.pl'
|
||||
|
||||
#python ../lib/get_dxcluster_modes.py /home/sysop/spider/data/bands.pl
|
||||
#if [ "$?" != "0" ]; then
|
||||
# echo 'ERROR on creating modes.json from dxspider bands.pl'
|
||||
@ -55,7 +183,7 @@ then
|
||||
fi
|
||||
|
||||
echo 'writing date in '${changelog} '...'
|
||||
#sed -i '1,4s/Date: [0-9][0-9]\/[0-9][0-9]\/[0-9][0-9][0-9][0-9]/Date: '`date '+%d\/%m\/%Y'`'/g' ${changelog}
|
||||
|
||||
if ! sed -i '1,4s/Date: [0-9][0-9]\/[0-9][0-9]\/[0-9][0-9][0-9][0-9]/Date: '$(date '+%d\/%m\/%Y')'/g' ${changelog}
|
||||
then
|
||||
echo 'ERROR writing date in '${changelog}
|
||||
@ -76,58 +204,5 @@ then
|
||||
exit 50
|
||||
fi
|
||||
|
||||
echo 'writing requirements...'
|
||||
if ! pip freeze|tee ../requirements.txt
|
||||
then
|
||||
echo 'ERROR wrinting requirements'
|
||||
exit 60
|
||||
fi
|
||||
|
||||
echo 'remove some packages...'
|
||||
sed -i '/certifi==/d' ../requirements.txt
|
||||
sed -i '/staticjinja==/d' ../requirements.txt
|
||||
|
||||
#used to minify the application javascript
|
||||
echo 'minify javascripts...'
|
||||
shopt -s extglob
|
||||
for i in ${path_static_js}/!(*[m][i][n].js|*.md)
|
||||
do
|
||||
[[ -e ${i} ]] || break # handle the case of no files found
|
||||
echo ${i}
|
||||
file_no_ext=$(basename "${i%.js}")
|
||||
if ! curl -X POST -s --fail --compressed --data "code_type=js" --data "js_engine=closure" --data-urlencode 'code@'${i} https://htmlcompressor.com/compress > ${path_static_js}/${file_no_ext}.min.js
|
||||
then
|
||||
echo 'ERROR minifying javascript: '${i}
|
||||
shopt -u extglob
|
||||
exit 80
|
||||
fi
|
||||
if [ ! -s "${path_static_js}/${file_no_ext}.min.js" ]; then
|
||||
echo "File is empty"
|
||||
shopt -u extglob
|
||||
exit 81
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
#used to minify css
|
||||
echo 'minify css...'
|
||||
for i in ${path_static_css}/!(*[m][i][n].css|*.md)
|
||||
do
|
||||
[[ -e ${i} ]] || break # handle the case of no files found
|
||||
echo ${i}
|
||||
file_no_ext=$(basename "${i%.css}")
|
||||
if ! curl -X POST -s --fail --compressed --data "code_type=css" --data-urlencode 'code@'${i} https://htmlcompressor.com/compress > ${path_static_css}/${file_no_ext}.min.css
|
||||
then
|
||||
echo 'ERROR minifying css: '${i}
|
||||
shopt -u extglob
|
||||
exit 90
|
||||
fi
|
||||
if [ ! -s "${path_static_css}/${file_no_ext}.min.css" ]; then
|
||||
echo "File is empty"
|
||||
shopt -u extglob
|
||||
exit 91
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
shopt -u extglob
|
||||
echo
|
||||
echo Build ok
|
||||
|
2
static/css/dev/README.md
Normal file
2
static/css/dev/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This folder `static/css/dev/` contains the style scripts for **development** environment.
|
||||
You can change these scripts as you want and rebuild the application
|
138
static/css/dev/style.css
Normal file
138
static/css/dev/style.css
Normal file
@ -0,0 +1,138 @@
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'bootstrap-icons';
|
||||
|
||||
}
|
||||
|
||||
.badge-responsive {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.text-responsive {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.badge-responsive {
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
#collapseFilters.collapsing {
|
||||
position: absolute !important;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
#collapseFilters.collapse.show {
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
max-height: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.img-flag {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px;
|
||||
padding: 3px;
|
||||
background-size: cover !important;
|
||||
max-width: auto;
|
||||
max-height: auto;
|
||||
height: 19px !important;
|
||||
width: 32px !important;
|
||||
-webkit-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
-moz-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
-o-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.ipcs {
|
||||
background-image: url("/static/images/background.webp");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.copyleft {
|
||||
display: inline-block;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
span.search-callsign {
|
||||
background: url(/static/images/search-callsign.svg) no-repeat top left;
|
||||
background-size: contain;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#input-group-callsign {
|
||||
margin-right: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#collapseFilters {
|
||||
margin-top: 10px;
|
||||
background-color: #DDE2E6;
|
||||
}
|
||||
|
||||
#spotsTable {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#band {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#dashboard {
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#telnet-thead {
|
||||
position: sticky;
|
||||
top: 0
|
||||
}
|
||||
|
||||
#form-band_activity {
|
||||
width: 600px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#chart-band_activity {
|
||||
width: 100%;
|
||||
height: 400px
|
||||
}
|
||||
|
||||
#chart-world_dx_spots_live {
|
||||
width: 600px;
|
||||
height: 500px;
|
||||
gap: 0px;
|
||||
}
|
||||
|
||||
#chart-hour_band {
|
||||
width: 600px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#silo-propagation-img {
|
||||
width: 480px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
#chart-dx_spots_x_month {
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
#chart-dx_spots_trend {
|
||||
width: 700px;
|
||||
height: 400px;
|
||||
}
|
2
static/css/rel/README.md
Normal file
2
static/css/rel/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This folder `static/css/rel/` contains the style scripts for **release** environment.
|
||||
You **CANNOT change** these scripts. Make changes only in `static/css/dev/` folder
|
1
static/css/rel/style.min.css
vendored
Normal file
1
static/css/rel/style.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");@font-face{font-display:swap;font-family:bootstrap-icons}.badge-responsive{width:70px}@media screen and (max-width:768px){.text-responsive{font-size:11px}.badge-responsive{width:40px}#collapseFilters.collapsing{position:absolute!important;z-index:20}#collapseFilters.collapse.show{display:block;position:absolute;z-index:20}.navbar-collapse{max-height:none!important}}.img-flag{background-color:#fff;background-size:cover!important;border:1px solid #ddd;border-radius:2px;-webkit-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-moz-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-o-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);height:19px!important;max-height:auto;max-width:auto;padding:3px;width:32px!important}.ipcs{background-image:url(/static/images/background.webp);background-repeat:no-repeat;background-size:cover}.copyleft{display:inline-block;transform:rotate(180deg)}span.search-callsign{background:url(/static/images/search-callsign.svg) no-repeat 0 0;background-size:contain;cursor:pointer;display:inline-block;height:16px;width:20px}#input-group-callsign{margin-bottom:.5rem;margin-right:1rem}#collapseFilters{background-color:#dde2e6;margin-top:10px}#spotsTable{margin-top:10px}#band{margin-top:5px}#dashboard{gap:10px;padding:10px}#telnet-thead{position:sticky;top:0}#form-band_activity{height:500px;width:600px}#chart-band_activity{height:400px;width:100%}#chart-world_dx_spots_live{gap:0;height:500px;width:600px}#chart-hour_band{height:500px;width:600px}#silo-propagation-img{height:400px;width:480px}#chart-dx_spots_x_month{height:400px;width:600px}#chart-dx_spots_trend{height:400px;width:700px}
|
@ -1,128 +0,0 @@
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'bootstrap-icons';
|
||||
|
||||
}
|
||||
|
||||
.badge-responsive {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.text-responsive {
|
||||
font-size: 11px;
|
||||
}
|
||||
.badge-responsive {
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
#collapseFilters.collapsing {
|
||||
position: absolute !important;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
#collapseFilters.collapse.show {
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
max-height: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.img-flag {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px;
|
||||
padding: 3px;
|
||||
background-size:cover !important;
|
||||
max-width: auto;
|
||||
max-height: auto;
|
||||
height: 19px !important;
|
||||
width: 32px !important;
|
||||
-webkit-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
-moz-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
-o-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.ipcs {
|
||||
background-image: url("/static/images/background.webp");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.copyleft {
|
||||
display: inline-block;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
span.search-callsign {
|
||||
background: url(/static/images/search-callsign.svg) no-repeat top left;
|
||||
background-size: contain;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#input-group-callsign {
|
||||
margin-right: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#collapseFilters {
|
||||
margin-top: 10px;
|
||||
background-color: #DDE2E6;
|
||||
}
|
||||
|
||||
#spotsTable {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#band {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#dashboard {
|
||||
padding:10px;
|
||||
gap:10px;
|
||||
}
|
||||
|
||||
#telnet-thead {
|
||||
position: sticky;top: 0
|
||||
}
|
||||
|
||||
#form-band_activity {
|
||||
width: 600px; height: 500px;
|
||||
}
|
||||
|
||||
#chart-band_activity {
|
||||
width:100%; height:400px
|
||||
}
|
||||
|
||||
#chart-world_dx_spots_live {
|
||||
width: 600px; height: 500px; gap: 0px;
|
||||
}
|
||||
|
||||
#chart-hour_band {
|
||||
width: 600px; height: 500px;
|
||||
}
|
||||
|
||||
#silo-propagation-img {
|
||||
width: 480px; height: 400px;
|
||||
}
|
||||
|
||||
#chart-dx_spots_x_month {
|
||||
width: 600px; height: 400px;
|
||||
}
|
||||
|
||||
#chart-dx_spots_trend {
|
||||
width: 700px; height: 400px;
|
||||
}
|
1
static/css/style.min.css
vendored
1
static/css/style.min.css
vendored
@ -1 +0,0 @@
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");@font-face{font-display:swap;font-family:'bootstrap-icons'}.badge-responsive{width:70px}@media screen and (max-width:768px){.text-responsive{font-size:11px}.badge-responsive{width:40px}}@media screen and (max-width:768px){#collapseFilters.collapsing{position:absolute!important;z-index:20}#collapseFilters.collapse.show{display:block;position:absolute;z-index:20}.navbar-collapse{max-height:none!important}}.img-flag{background-color:white;border:1px solid #ddd;border-radius:2px;padding:3px;background-size:cover!important;max-width:auto;max-height:auto;height:19px!important;width:32px!important;-webkit-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-moz-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-o-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5)}.ipcs{background-image:url("/static/images/background.webp");background-size:cover;background-repeat:no-repeat}.copyleft{display:inline-block;transform:rotate(180deg)}span.search-callsign{background:url(/static/images/search-callsign.svg) no-repeat top left;background-size:contain;cursor:pointer;display:inline-block;height:16px;width:20px}#input-group-callsign{margin-right:1rem;margin-bottom:.5rem}#collapseFilters{margin-top:10px;background-color:#dde2e6}#spotsTable{margin-top:10px}#band{margin-top:5px}#dashboard{padding:10px;gap:10px}#telnet-thead{position:sticky;top:0}#form-band_activity{width:600px;height:500px}#chart-band_activity{width:100%;height:400px}#chart-world_dx_spots_live{width:600px;height:500px;gap:0}#chart-hour_band{width:600px;height:500px}#silo-propagation-img{width:480px;height:400px}#chart-dx_spots_x_month{width:600px;height:400px}#chart-dx_spots_trend{width:700px;height:400px}
|
File diff suppressed because it is too large
Load Diff
1
static/html/dev/_base.html
Symbolic link
1
static/html/dev/_base.html
Symbolic link
@ -0,0 +1 @@
|
||||
../../../templates/_base.html
|
1
static/html/dev/offline.html
Symbolic link
1
static/html/dev/offline.html
Symbolic link
@ -0,0 +1 @@
|
||||
../../../templates/offline.html
|
@ -16,7 +16,7 @@
|
||||
<link rel="icon" href="/static/images/icons/spider_ico_master.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/images/icons/icon-apple.png">
|
||||
<link rel="manifest" href="/static/manifest.webmanifest">
|
||||
<link rel="stylesheet" href="/static/css/style.min.css">
|
||||
<link rel="stylesheet" href="/static/css/dev/style.css">
|
||||
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" as="style" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous" onload="this.rel='stylesheet' ">
|
||||
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"></noscript>
|
||||
@ -24,7 +24,7 @@
|
||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css" as="style" integrity="sha512-uvXdJud8WaOlQFjlz9B15Yy2Au/bMAvz79F7Xa6OakCl2jvQPdHD0hb3dEqZRdSwG4/sknePXlE7GiarwA/9Wg==" crossorigin="anonymous" onload="this.rel='stylesheet'" >
|
||||
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css"></noscript>
|
||||
|
||||
<script src="static/js/load_css.min.js"></script>
|
||||
<script src="static/js/dev/load_css.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
@ -47,7 +47,7 @@
|
||||
|
||||
</ul>
|
||||
<div id="MyClockDisplay" onload="showTime()" class="text-white-50 d-none d-lg-block"></div>
|
||||
<div class="text-white-50 d-none d-lg-block" >  (UTC)    </div>
|
||||
<div class="text-white-50 d-none d-lg-block" > (UTC) </div>
|
||||
|
||||
|
||||
</div>
|
||||
@ -61,8 +61,8 @@
|
||||
<div class="container-fluid mt-1 ml-0 mr-0 px-0">
|
||||
<div class="ipcs bg-light p-4 rounded-lg m-2">
|
||||
|
||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||
<p class="lead text-light">Spots list</p>
|
||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||
<p class="lead text-light">Spots list</p>
|
||||
|
||||
<p class="text-light">Telnet access: <a href="telnet://" class="text-white"></a></p>
|
||||
<p class="text-light">For connect your cluster, write to <a href="mailto:?Subject=Connect%20my%20DxCluster%20node" target="_top" class="text-white"></a></p>
|
||||
@ -73,13 +73,14 @@
|
||||
<!-- <div class="container">
|
||||
<div class="row">
|
||||
<div class="col align-self-center"> -->
|
||||
<div class="jumbotron alert alert-warning" role="alert">
|
||||
<div class="jumbotron alert alert-warning" role="alert">
|
||||
<h2 class="display-4">No internet connection</h2>
|
||||
<p class="lead">The features in this area require Internet connectivity. Please connect your computer to the Internet</p>
|
||||
<p class="lead">The features in this area require Internet connectivity. Please connect your computer to the
|
||||
Internet</p>
|
||||
<p class="lead">
|
||||
<a class="btn btn-primary btn-lg" href="/" role="button">Try again</a>
|
||||
</p>
|
||||
<!-- </div>
|
||||
<!-- </div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
@ -93,40 +94,43 @@
|
||||
<span id="version">v2.4.1</span>
|
||||
</div>
|
||||
</footer>
|
||||
<script async src="static/js/clock.min.js"></script>
|
||||
<script async src="static/js/copy_date.min.js"></script>
|
||||
<script async src="static/js/load-sw.min.js"></script>
|
||||
<script async src="static/js/dev/clock.js"></script>
|
||||
<script async src="static/js/dev/copy_date.js"></script>
|
||||
<script async src="static/js/dev/load-sw.js"></script>
|
||||
<script nonce="sedfGFG32xs">
|
||||
|
||||
|
||||
</script>
|
||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script defer src="static/js/dev/common.js"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
|
||||
|
||||
<script async src="static/js/callsign_search.min.js"></script>
|
||||
<script async src="static/js/dev/callsign_search.js"></script>
|
||||
|
||||
|
||||
|
||||
<!-- cookie consent management -->
|
||||
|
||||
|
||||
<div class="modal" tabindex="-1" id="cookie-consent-container">
|
||||
|
||||
<!-- Modal for cookie consent-->
|
||||
<div class="modal fade" id="cookie_consent_modal" tabindex="-1" aria-labelledby="cookie-consent-container" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title">We use cookies</h1>
|
||||
<h5 class="modal-title" id="exampleModalLabel">We use cookies</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>We use only technical cookies.</p>
|
||||
<p>Clicking "I agree", you agree to the storing of cookies on your device. To learn more about how we use cookies, please see our cookies policy.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="cookie-consent">I agree</button>
|
||||
<button type="button" class="btn btn-primary" id="cookie_consent_btn">I agree</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script defer src="static/js/cookie_consent.min.js"></script>
|
||||
|
||||
<script defer src="static/js/dev/cookie_consent.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
136
static/html/rel/offline.html
Normal file
136
static/html/rel/offline.html
Normal file
@ -0,0 +1,136 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<title>DX Cluster from IU1BOW: OFFLINE</title>
|
||||
<!-- page generated by staticjinja -->
|
||||
|
||||
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="Web Ham Radio DX Cluster and spot search">
|
||||
<meta name="keywords" content="ham radio, dx cluster, dx spots, cluster sposts,web dx cluster,dx cluster search, DX spots">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#2196f3">
|
||||
<link rel="icon" href="/static/images/icons/favicon.ico" size="any">
|
||||
<link rel="icon" href="/static/images/icons/spider_ico_master.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/images/icons/icon-apple.png">
|
||||
<link rel="manifest" href="/static/manifest.webmanifest">
|
||||
<link rel="stylesheet" href="/static/css/rel/style.min.css">
|
||||
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" as="style" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous" onload="this.rel='stylesheet' ">
|
||||
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"></noscript>
|
||||
|
||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css" as="style" integrity="sha512-uvXdJud8WaOlQFjlz9B15Yy2Au/bMAvz79F7Xa6OakCl2jvQPdHD0hb3dEqZRdSwG4/sknePXlE7GiarwA/9Wg==" crossorigin="anonymous" onload="this.rel='stylesheet'" >
|
||||
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css"></noscript>
|
||||
|
||||
<script src="static/js/rel/load_css.min.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
|
||||
|
||||
<!-- nav bar -->
|
||||
<nav class="navbar px-2 navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="/static/images/icons/icon-72x72.png" width="30" height="30" class="d-inline-block align-top" alt="">
|
||||
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" aria-controls="navbarToggler01" aria-expanded="false" aria-label="Toggle navigation" data-bs-toggle="collapse" data-bs-target="#navbarToggler01" >
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarToggler01">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0" >
|
||||
|
||||
</ul>
|
||||
<div id="MyClockDisplay" onload="showTime()" class="text-white-50 d-none d-lg-block"></div>
|
||||
<div class="text-white-50 d-none d-lg-block" > (UTC) </div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
|
||||
</header>
|
||||
<div class="container-fluid mt-1 ml-0 mr-0 px-0">
|
||||
<div class="ipcs bg-light p-4 rounded-lg m-2">
|
||||
|
||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||
<p class="lead text-light">Spots list</p>
|
||||
|
||||
<p class="text-light">Telnet access: <a href="telnet://" class="text-white"></a></p>
|
||||
<p class="text-light">For connect your cluster, write to <a href="mailto:?Subject=Connect%20my%20DxCluster%20node" target="_top" class="text-white"></a></p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- <div class="container">
|
||||
<div class="row">
|
||||
<div class="col align-self-center"> -->
|
||||
<div class="jumbotron alert alert-warning" role="alert">
|
||||
<h2 class="display-4">No internet connection</h2>
|
||||
<p class="lead">The features in this area require Internet connectivity. Please connect your computer to the
|
||||
Internet</p>
|
||||
<p class="lead">
|
||||
<a class="btn btn-primary btn-lg" href="/" role="button">Try again</a>
|
||||
</p>
|
||||
<!-- </div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<footer class="page-footer font-small blue">
|
||||
<div class="footer-copyright text-center py-3">
|
||||
<span class="copyleft">©</span> Copyleft:
|
||||
<span id="copyDate"></span>
|
||||
<a href="https://github.com/coulisse/spiderweb/" target="blank" rel="noopener">IU1BOW Spiderweb</a>
|
||||
<span id="version">v2.4.1</span>
|
||||
</div>
|
||||
</footer>
|
||||
<script async src="static/js/rel/clock.min.js"></script>
|
||||
<script async src="static/js/rel/copy_date.min.js"></script>
|
||||
<script async src="static/js/rel/load-sw.min.js"></script>
|
||||
<script nonce="sedfGFG32xs">
|
||||
|
||||
|
||||
</script>
|
||||
<script defer src="static/js/rel/common.min.js"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
|
||||
|
||||
<script async src="static/js/rel/callsign_search.min.js"></script>
|
||||
|
||||
|
||||
|
||||
<!-- cookie consent management -->
|
||||
|
||||
|
||||
|
||||
<!-- Modal for cookie consent-->
|
||||
<div class="modal fade" id="cookie_consent_modal" tabindex="-1" aria-labelledby="cookie-consent-container" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">We use cookies</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>We use only technical cookies.</p>
|
||||
<p>Clicking "I agree", you agree to the storing of cookies on your device. To learn more about how we use cookies, please see our cookies policy.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="cookie_consent_btn">I agree</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script defer src="static/js/rel/cookie_consent.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1 +0,0 @@
|
||||
Make changes only on file without '.min.' in their name, then minify using build.sh in 'script' folder
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* script loaded inline page in order to prepare data
|
||||
* for next operations
|
||||
*/
|
||||
//var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t",""));
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t",""));
|
||||
var rows_list = new Array();
|
||||
|
||||
var qryString = 'spotlist?c='+my_callsign;
|
||||
fetch(qryString)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
try {
|
||||
//rows_list = buildHtmlTable('bodyspot',data, rows_list, my_callsign);
|
||||
tb.build(data,my_callsign);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(err.stack);
|
||||
console.log(data);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
1
static/js/callsign_inline.min.js
vendored
1
static/js/callsign_inline.min.js
vendored
@ -1 +0,0 @@
|
||||
var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t","")),rows_list=[];buildHtmlTable("#bodyspot",payload_json,rows_list,my_callsign);
|
1
static/js/callsign_search.min.js
vendored
1
static/js/callsign_search.min.js
vendored
@ -1 +0,0 @@
|
||||
function myCallsignSearch(){callsign=document.getElementById("callsignInput").value;0<callsign.replace(/\s/g,"").length&&(location.href="/callsign.html?c=".concat(callsign.trim().toUpperCase()))};
|
@ -1,14 +0,0 @@
|
||||
function showTime(){
|
||||
var date=new Date()
|
||||
var utc = new Date(date.getTime() + date.getTimezoneOffset() * 60000);
|
||||
|
||||
var time = utc.toTimeString().split(' ')[0];
|
||||
time = time.split(':')[0]+':'+time.split(':')[1];
|
||||
document.getElementById("MyClockDisplay").innerText = time;
|
||||
document.getElementById("MyClockDisplay").textContent = time;
|
||||
|
||||
setTimeout(showTime, 1000);
|
||||
|
||||
}
|
||||
showTime();
|
||||
|
1
static/js/clock.min.js
vendored
1
static/js/clock.min.js
vendored
@ -1 +0,0 @@
|
||||
function showTime(){var a=new Date;a=(new Date(a.getTime()+6E4*a.getTimezoneOffset())).toTimeString().split(" ")[0];a=a.split(":")[0]+":"+a.split(":")[1];document.getElementById("MyClockDisplay").innerText=a;document.getElementById("MyClockDisplay").textContent=a;setTimeout(showTime,1E3)}showTime();
|
@ -1,167 +0,0 @@
|
||||
/**
|
||||
* Create a cookie
|
||||
*
|
||||
* @param cname {String} cookie name
|
||||
* @param cvalue {string} cookie value
|
||||
* @param exdays {integer} the number of the days for cookie expiration
|
||||
*/
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
|
||||
let expires = "expires=" + d.toUTCString();
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/" + ";Samesite=Strict;Secure=True";
|
||||
};
|
||||
|
||||
/**
|
||||
* get a cookie
|
||||
*
|
||||
* @param cname {String} cookie name
|
||||
* @returns cookie value
|
||||
*/
|
||||
function getCookie(cname) {
|
||||
let name = cname + "=";
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param date {String} date of the last refresh
|
||||
* @returns string formatted
|
||||
*/
|
||||
function get_last_refresh(data) {
|
||||
var dt_refresh = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
||||
var dt_refresh;
|
||||
dt_refresh.setUTCSeconds(data["last_refresh"]);
|
||||
const pad = (n, s = 2) => (`${new Array(s).fill(0)}${n}`).slice(-s);
|
||||
var hours = pad(dt_refresh.getHours());
|
||||
var minutes = pad(dt_refresh.getMinutes());
|
||||
var months_names = get_months_names();
|
||||
var month = months_names[dt_refresh.getMonth()];
|
||||
var day = dt_refresh.getDate();
|
||||
var year = dt_refresh.getFullYear();
|
||||
var last_refresh = "Data refresh: " + day + " of " + month + " " + year + " at " + hours + ":" + minutes;
|
||||
return last_refresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @returns {Array} list of months as short names
|
||||
*/
|
||||
function get_months_names() {
|
||||
var months_name = [];
|
||||
for (let monthNumber = 1; monthNumber < 13; monthNumber++) {
|
||||
const date = new Date();
|
||||
date.setMonth(monthNumber - 1);
|
||||
months_name.push(date.toLocaleString('en-US', {
|
||||
month: 'short'
|
||||
}));
|
||||
};
|
||||
return months_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param {Integer} num Number to be formatted
|
||||
* @returns {string} Number formatted with K for thousands and M for millions
|
||||
*/
|
||||
function format_u_k_m(num) {
|
||||
let label;
|
||||
let sign = 1;
|
||||
if (num < 0) {
|
||||
sign = -1;
|
||||
}
|
||||
let abs_num = Math.abs(num);
|
||||
if (abs_num == 0) {
|
||||
label = abs_num
|
||||
} else {
|
||||
if (abs_num > 0 && abs_num < 1000) {
|
||||
label = abs_num * sign;
|
||||
} else {
|
||||
if (abs_num >= 1000 && abs_num < 1000000) {
|
||||
label = abs_num / 1000 * sign + "K";
|
||||
} else {
|
||||
if (abs_num >= 1000000) {
|
||||
label = abs_num / 1000000 * sign + "M";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a selected element of a combo box with a value
|
||||
*
|
||||
* @param {string} id of the selected
|
||||
* @param {string} valueToSelect value to assign
|
||||
*/
|
||||
function selectElement(id, valueToSelect) {
|
||||
let element = document.getElementById(id);
|
||||
element.value = valueToSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event Handler to element (on)
|
||||
*
|
||||
* @param {string} id of the element
|
||||
* @param {string} eventType i.e. "change"
|
||||
* @param {function} handler the function to execute whene the evenet is raised
|
||||
*/
|
||||
function addEventHandler(id, eventType, handler) {
|
||||
if (id.addEventListener)
|
||||
id.addEventListener(eventType, handler, false);
|
||||
else if (elem.attachEvent)
|
||||
id.attachEvent('on' + eventType, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a text to a specific element
|
||||
*
|
||||
* @param {string} id of the element
|
||||
* @param {string} newvalue the value to assign to element
|
||||
*/
|
||||
function setText(id, newvalue) {
|
||||
var s = document.getElementById(id);
|
||||
s.innerHTML = newvalue;
|
||||
}
|
||||
|
||||
|
||||
function openModal() {
|
||||
document.getElementById("backdrop").style.display = "block"
|
||||
document.getElementById("exampleModal").style.display = "block"
|
||||
document.getElementById("exampleModal").classList.add("show")
|
||||
}
|
||||
function closeModal() {
|
||||
document.getElementById("backdrop").style.display = "none"
|
||||
document.getElementById("exampleModal").style.display = "none"
|
||||
document.getElementById("exampleModal").classList.remove("show")
|
||||
}
|
||||
|
||||
/*
|
||||
function doRefresh(){
|
||||
|
||||
var chartDom = document.getElementById('chart-dx_spots_trend');
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,'/plot_get_dx_spots_trend');
|
||||
|
||||
};
|
||||
|
||||
setInterval(function(){doRefresh()}, 5000);
|
||||
|
||||
*/
|
||||
|
1
static/js/cookie_consent.min.js
vendored
1
static/js/cookie_consent.min.js
vendored
@ -1 +0,0 @@
|
||||
var fn=function(){document.cookie="cookie_consent=true;SameSite=Strict; Secure;max-age=2592000";$("#cookie-consent-container").modal("hide")};document.getElementById("cookie-consent").onclick=fn;$("#cookie-consent-container").modal({backdrop:"static",keyboard:!1});$("#cookie-consent-container").modal("show");
|
@ -17,4 +17,4 @@ rules:
|
||||
- single
|
||||
semi:
|
||||
- error
|
||||
- never
|
||||
- always
|
2
static/js/dev/README.md
Normal file
2
static/js/dev/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This folder `static/js/dev/` contains the javascripts module for **development** environment.
|
||||
You can change these scripts as you want and rebuild the application
|
22
static/js/dev/callsign_inline.js
Normal file
22
static/js/dev/callsign_inline.js
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* script loaded inline page in order to prepare data
|
||||
* for next operations
|
||||
*/
|
||||
//var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t",""));
|
||||
var my_adxo_events = JSON.parse(my_adxo_events_json.replaceAll('\t', ''));
|
||||
|
||||
var qryString = 'spotlist?c=' + my_callsign;
|
||||
fetch(qryString)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
try {
|
||||
tb.build(data, my_callsign);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(err.stack);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
12
static/js/dev/clock.js
Normal file
12
static/js/dev/clock.js
Normal file
@ -0,0 +1,12 @@
|
||||
function showTime(){
|
||||
let date=new Date();
|
||||
let utc = new Date(date.getTime() + date.getTimezoneOffset() * 60000);
|
||||
let time = utc.toTimeString().split(' ')[0];
|
||||
time = time.split(':')[0]+':'+time.split(':')[1];
|
||||
document.getElementById("MyClockDisplay").innerText = time;
|
||||
document.getElementById("MyClockDisplay").textContent = time;
|
||||
setTimeout(showTime, 1000);
|
||||
}
|
||||
|
||||
showTime();
|
||||
|
155
static/js/dev/common.js
Normal file
155
static/js/dev/common.js
Normal file
@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Create a cookie
|
||||
*
|
||||
* @param cname {String} cookie name
|
||||
* @param cvalue {string} cookie value
|
||||
* @param exdays {integer} the number of the days for cookie expiration
|
||||
*/
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
|
||||
let expires = 'expires=' + d.toUTCString();
|
||||
document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/' + ';Samesite=Strict;Secure=True';
|
||||
};
|
||||
|
||||
/**
|
||||
* get a cookie
|
||||
*
|
||||
* @param cname {String} cookie name
|
||||
* @returns cookie value
|
||||
*/
|
||||
function getCookie(cname) {
|
||||
let name = cname + '=';
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param date {String} date of the last refresh
|
||||
* @returns string formatted
|
||||
*/
|
||||
function get_last_refresh(data) {
|
||||
let dt_refresh = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
||||
//var dt_refresh;
|
||||
dt_refresh.setUTCSeconds(data["last_refresh"]);
|
||||
const pad = (n, s = 2) => (`${new Array(s).fill(0)}${n}`).slice(-s);
|
||||
const hours = pad(dt_refresh.getHours());
|
||||
const minutes = pad(dt_refresh.getMinutes());
|
||||
const months_names = get_months_names();
|
||||
const month = months_names[dt_refresh.getMonth()];
|
||||
const day = dt_refresh.getDate();
|
||||
const year = dt_refresh.getFullYear();
|
||||
const last_refresh = "Data refresh: " + day + " of " + month + " " + year + " at " + hours + ":" + minutes;
|
||||
return last_refresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @returns {Array} list of months as short names
|
||||
*/
|
||||
function get_months_names() {
|
||||
var months_name = [];
|
||||
for (let monthNumber = 1; monthNumber < 13; monthNumber++) {
|
||||
const date = new Date();
|
||||
date.setMonth(monthNumber - 1);
|
||||
months_name.push(date.toLocaleString('en-US', {
|
||||
month: 'short'
|
||||
}));
|
||||
}
|
||||
return months_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param {Integer} num Number to be formatted
|
||||
* @returns {string} Number formatted with K for thousands and M for millions
|
||||
*/
|
||||
function format_u_k_m(num) {
|
||||
let label;
|
||||
let sign = 1;
|
||||
if (num < 0) {
|
||||
sign = -1;
|
||||
}
|
||||
let abs_num = Math.abs(num);
|
||||
if (abs_num == 0) {
|
||||
label = abs_num;
|
||||
} else {
|
||||
if (abs_num > 0 && abs_num < 1000) {
|
||||
label = abs_num * sign;
|
||||
} else {
|
||||
if (abs_num >= 1000 && abs_num < 1000000) {
|
||||
label = abs_num / 1000 * sign + 'K';
|
||||
} else {
|
||||
if (abs_num >= 1000000) {
|
||||
label = abs_num / 1000000 * sign + 'M';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a selected element of a combo box with a value
|
||||
*
|
||||
* @param {string} id of the selected
|
||||
* @param {string} valueToSelect value to assign
|
||||
*/
|
||||
function selectElement(id, valueToSelect) {
|
||||
let element = document.getElementById(id);
|
||||
element.value = valueToSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event Handler to element (on)
|
||||
*
|
||||
* @param {string} id of the element
|
||||
* @param {string} eventType i.e. "change"
|
||||
* @param {function} handler the function to execute whene the evenet is raised
|
||||
*/
|
||||
function addEventHandler(id, eventType, handler) {
|
||||
if (id.addEventListener)
|
||||
id.addEventListener(eventType, handler, false);
|
||||
else if (id.attachEvent)
|
||||
id.attachEvent('on' + eventType, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a text to a specific element
|
||||
*
|
||||
* @param {string} id of the element
|
||||
* @param {string} newvalue the value to assign to element
|
||||
*/
|
||||
function setText(id, newvalue) {
|
||||
var s = document.getElementById(id);
|
||||
s.innerHTML = newvalue;
|
||||
}
|
||||
|
||||
/*
|
||||
function doRefresh(){
|
||||
|
||||
var chartDom = document.getElementById('chart-dx_spots_trend');
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,'/plot_get_dx_spots_trend');
|
||||
|
||||
};
|
||||
|
||||
setInterval(function(){doRefresh()}, 5000);
|
||||
|
||||
*/
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
script used to acquire user conset to cookie banner (and set the cookie consent)
|
||||
script used to acquire user consent to cookie banner (and set the cookie consent)
|
||||
*/
|
||||
let cookie_modal = new bootstrap.Modal(document.getElementById('cookie_consent_modal'), {
|
||||
keyboard: false
|
||||
})
|
||||
});
|
||||
cookie_modal.show();
|
||||
|
||||
//if button is pressed, setting cookie
|
@ -1 +1 @@
|
||||
document.getElementById('copyDate').innerHTML='2020-'.concat(new Date().getFullYear())
|
||||
document.getElementById('copyDate').innerHTML='2020-'.concat(new Date().getFullYear());
|
@ -5,8 +5,7 @@
|
||||
* Moreover, add an event to the button of the filter form
|
||||
*/
|
||||
//var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t",""));
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t",""));
|
||||
var rows_list = new Array();
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll('\t',''));
|
||||
|
||||
refresh_timer(); //run first data fetch
|
||||
var myRefresh = setInterval(refresh_timer, timer_interval_json);
|
17
static/js/dev/load-sw.js
Normal file
17
static/js/dev/load-sw.js
Normal file
@ -0,0 +1,17 @@
|
||||
/* this is used to instance service worker for PWA
|
||||
* Check compatibility for the browser we're running this in
|
||||
*/
|
||||
if ('serviceWorker' in navigator) {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
console.log('[PWA Builder] active service worker found, no need to register');
|
||||
} else {
|
||||
// Register the service worker
|
||||
navigator.serviceWorker
|
||||
.register('service-worker.js', {
|
||||
scope: './'
|
||||
})
|
||||
.then(function (reg) {
|
||||
console.log('[PWA Builder] Service worker has been registered for scope: ' + reg.scope);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*! loadCSS. [c]2020 Filament Group, Inc. MIT License */
|
||||
(function(w){
|
||||
"use strict";
|
||||
'use strict';
|
||||
/* exported loadCSS */
|
||||
var loadCSS = function( href, before, media, attributes ){
|
||||
// Arguments explained:
|
||||
@ -10,13 +10,13 @@
|
||||
// `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all'
|
||||
// `attributes` [OPTIONAL] is the Object of attribute name/attribute value pairs to set on the stylesheet's DOM Element.
|
||||
var doc = w.document;
|
||||
var ss = doc.createElement( "link" );
|
||||
var ss = doc.createElement( 'link' );
|
||||
var ref;
|
||||
if( before ){
|
||||
ref = before;
|
||||
}
|
||||
else {
|
||||
var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes;
|
||||
var refs = ( doc.body || doc.getElementsByTagName( 'head' )[ 0 ] ).childNodes;
|
||||
ref = refs[ refs.length - 1];
|
||||
}
|
||||
|
||||
@ -29,10 +29,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
ss.rel = "stylesheet";
|
||||
ss.rel = 'stylesheet';
|
||||
ss.href = href;
|
||||
// temporarily set media to something inapplicable to ensure it'll fetch without blocking render
|
||||
ss.media = "only x";
|
||||
ss.media = 'only x';
|
||||
|
||||
// wait until body is defined before injecting link. This ensures a non-blocking load in IE11.
|
||||
function ready( cb ){
|
||||
@ -65,24 +65,24 @@
|
||||
|
||||
function loadCB(){
|
||||
if( ss.addEventListener ){
|
||||
ss.removeEventListener( "load", loadCB );
|
||||
ss.removeEventListener( 'load', loadCB );
|
||||
}
|
||||
ss.media = media || "all";
|
||||
ss.media = media || 'all';
|
||||
}
|
||||
|
||||
// once loaded, set link's media back to `all` so that the stylesheet applies once it loads
|
||||
if( ss.addEventListener ){
|
||||
ss.addEventListener( "load", loadCB);
|
||||
ss.addEventListener( 'load', loadCB);
|
||||
}
|
||||
ss.onloadcssdefined = onloadcssdefined;
|
||||
onloadcssdefined( loadCB );
|
||||
return ss;
|
||||
};
|
||||
// commonjs
|
||||
if( typeof exports !== "undefined" ){
|
||||
if( typeof exports !== 'undefined' ){
|
||||
exports.loadCSS = loadCSS;
|
||||
}
|
||||
else {
|
||||
w.loadCSS = loadCSS;
|
||||
}
|
||||
}( typeof global !== "undefined" ? global : this ));
|
||||
}( typeof global !== 'undefined' ? global : this ));
|
191
static/js/dev/plot_band_activity.js
Normal file
191
static/js/dev/plot_band_activity.js
Normal file
@ -0,0 +1,191 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build band_activity chart
|
||||
* ******************************************************************************/
|
||||
class band_activity {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {string} region This is the continent name (EU,AF,NA...) of the selected
|
||||
* @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF)
|
||||
* @param {Json} continents The continent list( EU, SA, ...)
|
||||
*/
|
||||
refresh(my_chart, end_point, region, bands, continents){
|
||||
// Asynchronous Data Loading
|
||||
fetch(end_point+'?continent='+region)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=Array.from(data['band activity']).map(function (item) {
|
||||
return [item[1], item[0], item[2] || '-'];
|
||||
});
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
position: 'top',
|
||||
formatter: function (p) {
|
||||
var format = p.seriesName +' on ' + p.name +' band: '+'<strong>'+p.data[2]+'</strong>';
|
||||
return format;
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text:'Band activity',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
right: 'right',
|
||||
top : 'bottom',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
height: '80%',
|
||||
left: 25,
|
||||
top: 50,
|
||||
right: 60,
|
||||
bottom: 0,
|
||||
show: true,
|
||||
backgroundColor: 'rgb(255, 255, 255)',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: bands,
|
||||
axisTick: {
|
||||
show: true,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: continents,
|
||||
axisTick: {
|
||||
show: true,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
calculable: true,
|
||||
orient: 'vertical',
|
||||
right: 'right',
|
||||
top: 'center',
|
||||
min: 0,
|
||||
max: 30,
|
||||
inRange : {
|
||||
color: ['#ffffe6','yellow','red']
|
||||
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Spots',
|
||||
type: 'heatmap',
|
||||
data: dataMap,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(100, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} cont_cq The continent list( EU, SA, ...)
|
||||
* @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
constructor(chart_id, end_point, cont_cq, band_freq) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
|
||||
//populate continents array
|
||||
var continents=[];
|
||||
cont_cq.forEach(function myFunction(item, index) {
|
||||
continents[index]=item['id'];
|
||||
});
|
||||
|
||||
//populate bands array
|
||||
var bands=[];
|
||||
band_freq.forEach(function myFunction(item, index) {
|
||||
bands[index]=item['id'];
|
||||
});
|
||||
|
||||
//managing region
|
||||
var selectedContinent=getCookie('user_region');
|
||||
var selectedContinent_desc=getCookie('user_region_desc');
|
||||
if (!selectedContinent) {
|
||||
selectedContinent='EU';
|
||||
selectedContinent_desc='Europe';
|
||||
setCookie('user_region',selectedContinent,60);
|
||||
setCookie('user_region_desc',selectedContinent_desc,60);
|
||||
};
|
||||
|
||||
selectElement('continentInput', selectedContinent);
|
||||
|
||||
addEventHandler(document.getElementById('continentInput'), 'change', function() {
|
||||
selectedContinent=this.value;
|
||||
selectedContinent_desc=this.options[this.selectedIndex].text;
|
||||
setCookie('user_region',selectedContinent,60);
|
||||
setCookie('user_region_desc',selectedContinent_desc,60);
|
||||
plot_ba.refresh(myChart, end_point, selectedContinent,bands,continents);
|
||||
setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band');
|
||||
});
|
||||
|
||||
setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band');
|
||||
|
||||
this.refresh(myChart, end_point, selectedContinent,bands,continents);
|
||||
|
||||
/* resize chart*/
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_ba = new band_activity ('chart-band_activity','/plot_get_heatmap_data',continents_cq,band_frequencies);
|
||||
|
||||
/*setInterval(function(){doRefresh('chart-band_activity','/plot_get_heatmap_data')}, 5000);
|
||||
|
||||
function doRefresh(chart_id,end_point){
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,end_point);
|
||||
};
|
||||
|
||||
*/
|
||||
|
142
static/js/dev/plot_dx_spots_per_month.js
Normal file
142
static/js/dev/plot_dx_spots_per_month.js
Normal file
@ -0,0 +1,142 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build dx spots per month chart
|
||||
* ******************************************************************************/
|
||||
|
||||
class dx_spots_per_month {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
//$.getJSON(end_point).done(function(data) {
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var year_now = new Date().getFullYear();
|
||||
var year_0 = (year_now - 0).toString();
|
||||
var year_1 = (year_now - 1).toString();
|
||||
var year_2 = (year_now - 2).toString();
|
||||
|
||||
var months_name = get_months_names();
|
||||
|
||||
var dataMap_year_0=[];
|
||||
var dataMap_year_1=[];
|
||||
var dataMap_year_2=[];
|
||||
for (let i = 1; i < 13; i++) {
|
||||
dataMap_year_0.push(data.spots_per_month[i].year_0);
|
||||
dataMap_year_1.push(data.spots_per_month[i].year_1);
|
||||
dataMap_year_2.push(data.spots_per_month[i].year_2);
|
||||
}
|
||||
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: 'DX SPOTS per month',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
legend: {
|
||||
data: [year_2, year_1, year_0],
|
||||
bottom: 'bottom'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar', 'stack'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: months_name
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (function (value){
|
||||
return format_u_k_m(value);
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: year_2,
|
||||
type: 'bar',
|
||||
barGap: 0,
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_2
|
||||
},
|
||||
{
|
||||
name: year_1,
|
||||
type: 'bar',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_1
|
||||
|
||||
},
|
||||
{
|
||||
name: year_0,
|
||||
type: 'bar',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_0
|
||||
},
|
||||
]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_dspm = new dx_spots_per_month ('chart-dx_spots_x_month','/plot_get_dx_spots_per_month');
|
136
static/js/dev/plot_dx_spots_trend.js
Normal file
136
static/js/dev/plot_dx_spots_trend.js
Normal file
@ -0,0 +1,136 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build dx spots trend
|
||||
* ******************************************************************************/
|
||||
|
||||
class dx_spots_trend {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
//$.getJSON(end_point).done(function(data) {
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=[];
|
||||
for (const [key, value] of Object.entries(data['spots_trend'])) {
|
||||
var tuple=[];
|
||||
tuple.push(key);
|
||||
tuple.push(value);
|
||||
dataMap.push(tuple);
|
||||
}
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
position: function (pt) {
|
||||
return [pt[0], '10%'];
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: 'DX SPOTS trend',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
saveAsImage: {},
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '10%'],
|
||||
axisLabel: {
|
||||
formatter: (function (value){
|
||||
return format_u_k_m(value);
|
||||
})
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 65,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 20
|
||||
},
|
||||
],
|
||||
|
||||
series: [
|
||||
{
|
||||
name: 'Spots',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
itemStyle: {
|
||||
color: '#078513'
|
||||
},
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#57fa75'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#118226'
|
||||
}
|
||||
])
|
||||
},
|
||||
data: dataMap
|
||||
}
|
||||
]
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_dst = new dx_spots_trend ('chart-dx_spots_trend','/plot_get_dx_spots_trend');
|
152
static/js/dev/plot_hour_band.js
Normal file
152
static/js/dev/plot_hour_band.js
Normal file
@ -0,0 +1,152 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build band/hour chart
|
||||
* ******************************************************************************/
|
||||
|
||||
class hour_band {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
refresh(my_chart,end_point,bands) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the dat
|
||||
var last_refresh=get_last_refresh(data);
|
||||
//set hour indicator names
|
||||
var hour_indicators=[];
|
||||
|
||||
//for (let i = 0; i < 24; i++) {
|
||||
for (let i = 23; i > -1; i--) {
|
||||
var hour_name={};
|
||||
var s=i.toString();
|
||||
hour_name['name']=s;
|
||||
hour_indicators.push(hour_name);
|
||||
}
|
||||
|
||||
//cycling whithin each bands and hours
|
||||
var dataMap=[];
|
||||
bands.forEach(band_item => {
|
||||
var qso=[];
|
||||
|
||||
//for (let i = 0; i < 24; i++) {
|
||||
for (let i = 23; i > -1; i--) {
|
||||
try {
|
||||
var value=data['hour_band'][band_item][i];
|
||||
if (typeof value == 'undefined') {
|
||||
value = 0;
|
||||
}
|
||||
qso.push(value);
|
||||
} catch (TypeError) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
var tot={'value':qso,'name':band_item};
|
||||
dataMap.push(tot);
|
||||
});
|
||||
|
||||
|
||||
//options
|
||||
my_chart.setOption({
|
||||
legend: {
|
||||
|
||||
orient: 'horizontal',
|
||||
left: 'left',
|
||||
bottom: 'bottom'
|
||||
},
|
||||
title: {
|
||||
text: 'DX SPOTS per hour in last month',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
right: 'right',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
radar: {
|
||||
shape: 'circle',
|
||||
//startAngle: 285, //0 on left side
|
||||
startAngle: 105, //0 on top
|
||||
indicator: hour_indicators,
|
||||
center: ['47%', '46%'],
|
||||
axisName: {
|
||||
color: 'rgb(80,80,80)',
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
type: 'radar',
|
||||
symbol: 'none',
|
||||
data: dataMap,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params) => {
|
||||
return 'Band: '+params.name;
|
||||
},
|
||||
},
|
||||
emphasis: {
|
||||
lineStyle: {
|
||||
width: 4
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
constructor(chart_id,end_point,band_freq) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
|
||||
//populate bands array
|
||||
var bands=[];
|
||||
band_freq.forEach(function myFunction(item, index) {
|
||||
bands[index]=item['id']
|
||||
});
|
||||
|
||||
this.refresh(myChart,end_point,bands);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_hb = new hour_band('chart-hour_band','/plot_get_hour_band',band_frequencies);
|
161
static/js/dev/plot_world_dx_spots_live.js
Normal file
161
static/js/dev/plot_world_dx_spots_live.js
Normal file
@ -0,0 +1,161 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build world dx spots live
|
||||
* ******************************************************************************/
|
||||
|
||||
class world_dx_spots_live {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
fetch('world.json')
|
||||
.then(response => response.text())
|
||||
.then(geoJson => {
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=[];
|
||||
data['world_dx_spots_live'].forEach(function myFunction(item, index) {
|
||||
//lon, lat, number of qso
|
||||
dataMap.push({'value':[item['lat'],item['lon'],item['count']]});
|
||||
});
|
||||
|
||||
my_chart.hideLoading();
|
||||
echarts.registerMap('WR', geoJson);
|
||||
|
||||
my_chart.setOption( {
|
||||
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 50,
|
||||
inRange: {
|
||||
symbolSize: [5, 20]
|
||||
}
|
||||
},
|
||||
|
||||
geo: {
|
||||
type: 'map',
|
||||
map: 'WR',
|
||||
roam: true,
|
||||
zoom: 1.2,
|
||||
aspectScale: 0.70,
|
||||
layoutCenter: ['50%', '54%'],
|
||||
layoutSize: '100%',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#323c48',
|
||||
borderColor: '#111'
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#2a333d'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function(val) {
|
||||
var out='Spots: <STRONG>'+ val.value[2] +'</STRONG>';
|
||||
return out;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
iconStyle: {
|
||||
borderColor: '#fff',
|
||||
},
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
//backgroundColor: '#404a59',
|
||||
backgroundColor: '#596475',
|
||||
title: {
|
||||
text: 'World DX SPOTS in last hour',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
right:'right',
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
subtextStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
data:dataMap,
|
||||
label: {
|
||||
emphasis: {
|
||||
position: 'right',
|
||||
show: false
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#eea638'
|
||||
}
|
||||
},
|
||||
/*
|
||||
symbolSize: function (val) {
|
||||
return val[2] / 4;
|
||||
},
|
||||
*/
|
||||
}
|
||||
]
|
||||
|
||||
}); //end options
|
||||
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_wdsl = new world_dx_spots_live ('chart-world_dx_spots_live','/plot_get_world_dx_spots_live');
|
360
static/js/dev/table.js
Normal file
360
static/js/dev/table.js
Normal file
@ -0,0 +1,360 @@
|
||||
class table_builder {
|
||||
/**
|
||||
* Table builder constructor
|
||||
* @param selector {string} The html identifier where build the spots table
|
||||
*/
|
||||
constructor(selector) {
|
||||
this.selector = selector;
|
||||
this.current_data = [];
|
||||
}
|
||||
/**
|
||||
* @return last_rowid {integer} the last rowid
|
||||
*/
|
||||
getLastRowId() {
|
||||
let last_rowid;
|
||||
if (this.current_data == null) {
|
||||
last_rowid = 0;
|
||||
} else {
|
||||
if (this.current_data.length < 1) {
|
||||
last_rowid = 0;
|
||||
} else {
|
||||
last_rowid = this.current_data[0].rowid;
|
||||
}
|
||||
}
|
||||
return last_rowid;
|
||||
}
|
||||
|
||||
resetData() {
|
||||
this.current_data = [];
|
||||
}
|
||||
/**
|
||||
* @param line {object} with the data of a single spot
|
||||
* @param isnew {boolean} is the new rows indicator
|
||||
* @param dt_current {string} current date in dd/mm/yyyy format
|
||||
* @param callsign {string} optional callsign
|
||||
* @return a <tr> dom element
|
||||
*/
|
||||
#buildRow(line, isnew, dt_current, callsign = '') {
|
||||
const row = document.createElement('tr');
|
||||
row.id = line.rowid;
|
||||
|
||||
if (callsign.length > 0) {
|
||||
if (callsign == line.de) {
|
||||
row.id = line.rowid;
|
||||
} else if (callsign == line.dx) {
|
||||
row.id = line.rowid;
|
||||
}
|
||||
} else if (isnew) {
|
||||
row.className = 'table-info';
|
||||
}
|
||||
|
||||
//Column: DE search on QRZ
|
||||
const i_qrzde = document.createElement('i');
|
||||
i_qrzde.className = 'bi-search';
|
||||
i_qrzde.role = 'button';
|
||||
i_qrzde.ariaLabel = line.de;
|
||||
const a_qrzde = document.createElement('a');
|
||||
a_qrzde.href = qrz_url + line.de;
|
||||
a_qrzde.target = '_blank';
|
||||
a_qrzde.rel = 'noopener';
|
||||
const span_qrzde = document.createElement('span');
|
||||
|
||||
//Mark DE if it found in callsign search
|
||||
if (line.de == callsign) {
|
||||
const mark_qrzde = document.createElement('mark');
|
||||
mark_qrzde.textContent = line.de;
|
||||
span_qrzde.appendChild(mark_qrzde);
|
||||
} else {
|
||||
span_qrzde.textContent = '\xa0' + line.de;
|
||||
}
|
||||
|
||||
const td_qrzde = document.createElement('td');
|
||||
|
||||
a_qrzde.appendChild(i_qrzde);
|
||||
td_qrzde.appendChild(a_qrzde);
|
||||
td_qrzde.appendChild(span_qrzde);
|
||||
row.append(td_qrzde);
|
||||
|
||||
//Column: frequency
|
||||
var freq = Intl.NumberFormat('it-IT', {
|
||||
style: 'decimal'
|
||||
}).format(line.freq);
|
||||
|
||||
const span_freq = document.createElement('span');
|
||||
span_freq.className = 'badge bg-warning text-dark badge-responsive';
|
||||
span_freq.textContent = freq;
|
||||
|
||||
const td_freq = document.createElement('td');
|
||||
td_freq.appendChild(span_freq);
|
||||
|
||||
row.appendChild(td_freq);
|
||||
|
||||
//Column: DX (with ADXO Management)
|
||||
var adxo = findAdxo(my_adxo_events, line.dx);
|
||||
var adxo_link = '<a href=' + adxo_url + ' target=_blank rel=noopener >NG3K Website</a>';
|
||||
const i_qrzdx = document.createElement('i');
|
||||
i_qrzdx.className = 'bi-search';
|
||||
i_qrzdx.role = 'button';
|
||||
i_qrzdx.ariaLabel = line.dx;
|
||||
const a_qrzdx = document.createElement('a');
|
||||
a_qrzdx.href = qrz_url + line.dx;
|
||||
a_qrzdx.target = '_blank';
|
||||
a_qrzdx.rel = 'noopener';
|
||||
const span_qrzdx = document.createElement('span');
|
||||
|
||||
//Mark DX if it found in callsign search
|
||||
const mark_qrzdx = document.createElement('mark');
|
||||
mark_qrzdx.textContent = line.dx;
|
||||
if (line.dx == callsign) {
|
||||
span_qrzdx.appendChild(mark_qrzdx);
|
||||
} else {
|
||||
span_qrzdx.textContent = '\xa0' + line.dx;
|
||||
}
|
||||
|
||||
if (adxo != undefined) {
|
||||
const i_adxo = document.createElement('i');
|
||||
i_adxo.tabIndex = 0;
|
||||
i_adxo.className = 'bi-megaphone-fill';
|
||||
i_adxo.style = 'color: cornflowerblue;';
|
||||
i_adxo.role = 'button';
|
||||
i_adxo.ariaLabel = 'dx_operations';
|
||||
i_adxo.setAttribute('data-bs-container', 'body');
|
||||
i_adxo.setAttribute('data-bs-toggle', 'popover');
|
||||
i_adxo.setAttribute('data-bs-trigger', 'focus');
|
||||
i_adxo.setAttribute('data-bs-sanitizer', 'true');
|
||||
i_adxo.setAttribute('data-bs-placement', 'auto');
|
||||
i_adxo.setAttribute('data-bs-html', 'true');
|
||||
i_adxo.setAttribute('data-bs-title', 'Announced DX Op.: ' + adxo.summary);
|
||||
i_adxo.setAttribute('data-bs-content', adxo.description + 'data from ' + adxo_link);
|
||||
span_qrzdx.appendChild(i_adxo);
|
||||
}
|
||||
|
||||
const td_qrzdx = document.createElement('td');
|
||||
a_qrzdx.appendChild(i_qrzdx);
|
||||
td_qrzdx.appendChild(a_qrzdx);
|
||||
td_qrzdx.append(span_qrzdx);
|
||||
row.appendChild(td_qrzdx);
|
||||
|
||||
//Column: Flag
|
||||
try {
|
||||
const span_flag = document.createElement('span');
|
||||
span_flag.className = 'img-flag fi fi-' + line.iso;
|
||||
span_flag.setAttribute('data-bs-container', 'body');
|
||||
span_flag.setAttribute('data-bs-toggle', 'popover');
|
||||
span_flag.setAttribute('data-bs-trigger', 'hover');
|
||||
span_flag.setAttribute('data-bs-placement', 'left');
|
||||
span_flag.setAttribute('data-bs-content', line.country);
|
||||
|
||||
const td_flag = document.createElement('td');
|
||||
td_flag.appendChild(span_flag);
|
||||
row.appendChild(td_flag);
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log('error creating flag');
|
||||
const td_flag = document.createElement('td');
|
||||
row.appendChild(td_flag);
|
||||
}
|
||||
|
||||
//Column: Country
|
||||
const td_country_code = document.createElement('td');
|
||||
td_country_code.className = 'd-none d-lg-table-cell d-xl-table-cell';
|
||||
td_country_code.textContent = line.country;
|
||||
row.appendChild(td_country_code);
|
||||
|
||||
//Column: Comment
|
||||
const td_comm = document.createElement('td');
|
||||
td_comm.className = 'd-none d-lg-table-cell d-xl-table-cell';
|
||||
td_comm.textContent = line.comm;
|
||||
row.appendChild(td_comm);
|
||||
|
||||
//Column: UTC
|
||||
let dt = new Date(line.time * 1000);
|
||||
let hh = '00' + dt.getUTCHours();
|
||||
hh = hh.substring(hh.length - 2, hh.length);
|
||||
let mi = '00' + dt.getMinutes();
|
||||
mi = mi.substring(mi.length - 2, mi.length);
|
||||
let dd = '00' + dt.getUTCDate();
|
||||
dd = dd.substring(dd.length - 2, dd.length);
|
||||
let mo = '00' + (Number(dt.getUTCMonth()) + 1);
|
||||
mo = mo.substring(mo.length - 2, mo.length);
|
||||
let yy = dt.getUTCFullYear();
|
||||
let tm = hh + ':' + mi;
|
||||
dt = dd + '/' + mo + '/' + yy;
|
||||
|
||||
const div_date_time = document.createElement('div');
|
||||
div_date_time.className = 'd-flex flex-column';
|
||||
const p_time = document.createElement('div');
|
||||
p_time.textContent = tm;
|
||||
div_date_time.appendChild(p_time);
|
||||
if (dt != dt_current) {
|
||||
const p_date = document.createElement('div');
|
||||
p_date.textContent = dt;
|
||||
div_date_time.appendChild(p_date);
|
||||
}
|
||||
|
||||
row.appendChild(div_date_time);
|
||||
|
||||
//Finally append the row created to the table
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the table with the spot
|
||||
*
|
||||
* @param data {json} The payload with all the spots received from cluster
|
||||
* @param rl {json} Row List
|
||||
* @param callsign {string} An optional parameter with the callsign to search
|
||||
*/
|
||||
build(data, callsign) {
|
||||
|
||||
if (data != null) {
|
||||
|
||||
//get current date
|
||||
let d = new Date();
|
||||
let dd_current = '00' + d.getUTCDate();
|
||||
dd_current = dd_current.substring(dd_current.length - 2, dd_current.length);
|
||||
let mo_current = '00' + (Number(d.getUTCMonth()) + 1);
|
||||
mo_current = mo_current.substring(mo_current.length - 2, mo_current.length);
|
||||
let yy_current = d.getUTCFullYear();
|
||||
let dt_current = dd_current + '/' + mo_current + '/' + yy_current;
|
||||
|
||||
//empty the table
|
||||
document.getElementById(this.selector).replaceChildren();
|
||||
|
||||
//insert in table new elements
|
||||
let merge_data = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
document.getElementById(this.selector).append(this.#buildRow(data[i], true, dt_current, callsign));
|
||||
merge_data.push(data[i]);
|
||||
}
|
||||
|
||||
//insert in html table previous elements
|
||||
for (let i = 0; i < this.current_data.length - data.length; i++) {
|
||||
document.getElementById(this.selector).append(this.#buildRow(this.current_data[i], false, dt_current, callsign));
|
||||
merge_data.push(this.current_data[i]);
|
||||
}
|
||||
|
||||
//replace current data with merge
|
||||
this.current_data = merge_data;
|
||||
|
||||
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
|
||||
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
|
||||
return new bootstrap.Popover(popoverTriggerEl);
|
||||
});
|
||||
}
|
||||
}
|
||||
} //end class
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
* javascript used to popolate main table with spots
|
||||
* ******************************************************************************/
|
||||
const adxo_url = 'https://www.ng3k.com/misc/adxo.html';
|
||||
const qrz_url = 'https://www.qrz.com/db/';
|
||||
const tb = new table_builder('bodyspot');
|
||||
var qryAll_sv = '';
|
||||
|
||||
/**
|
||||
* Decode Announced Dx Operation (ng3k)
|
||||
*
|
||||
* @param adxo {adxo} This is the json containing all the dxo events
|
||||
* @param callsign_to_find {callsign_to_find} The callsign of the current dx line
|
||||
*/
|
||||
function findAdxo(adxo, callsign_to_find) {
|
||||
if (adxo) {
|
||||
for (let i = 0; i < adxo.length; i++) {
|
||||
if (adxo[i].callsign == callsign_to_find) {
|
||||
return adxo[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Function to filter spot when pressed the search button on filter
|
||||
* This function trigger the search, also triggered by timer
|
||||
*/
|
||||
function mySearch(event) {
|
||||
event.preventDefault();
|
||||
refresh_timer(); //force the call of query
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for construct query string for single value selection
|
||||
*
|
||||
* @param id {string} The html identifier used for filter
|
||||
* @param param {string}the parameter for the query
|
||||
* @param len {number} The maximum number of element that could be selected; use -1 if the filter permits a single selection
|
||||
* @param qrystr {string} Th initial query string to be completed with the new filter
|
||||
*/
|
||||
function getFilter(id, param, len, qrystr) {
|
||||
|
||||
let selectedFilter = [].map.call(document.getElementById(id).selectedOptions, option => option.value);
|
||||
let qryFilter = '';
|
||||
if (selectedFilter.length < len || len == -1) {
|
||||
qryFilter = selectedFilter.map(function (el) {
|
||||
if (el) {
|
||||
return param + '=' + el;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}).join('&');
|
||||
qrystr = qrystr.concat('&'.concat(qryFilter));
|
||||
if (qrystr.substring(0, 1) == '&') {
|
||||
qrystr = qrystr.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
return qrystr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search / Filter cluster spot based on filter settings
|
||||
* Gets the filter values, constructs the query parameter and
|
||||
* make the request to the server
|
||||
*/
|
||||
function refresh_timer() {
|
||||
|
||||
let qryAll = '';
|
||||
|
||||
//get other filters
|
||||
qryAll = getFilter('band', 'b', 14, qryAll);
|
||||
qryAll = getFilter('de_re', 'e', 7, qryAll);
|
||||
qryAll = getFilter('dx_re', 'x', 7, qryAll);
|
||||
qryAll = getFilter('mode', 'm', 3, qryAll);
|
||||
qryAll = getFilter('cqdeInput', 'qe', -1, qryAll);
|
||||
qryAll = getFilter('cqdxInput', 'qx', -1, qryAll);
|
||||
|
||||
//Composing query string
|
||||
let qryString;
|
||||
|
||||
//If the filter is changed we need to reset the data table and restart from rowid=0
|
||||
if (qryAll != qryAll_sv) {
|
||||
tb.resetData();
|
||||
qryAll_sv = qryAll;
|
||||
}
|
||||
|
||||
qryString = ('spotlist?lr=').concat(tb.getLastRowId());
|
||||
|
||||
if (qryAll) {
|
||||
qryString = qryString.concat('&'.concat(qryAll));
|
||||
}
|
||||
|
||||
|
||||
console.log(qryString);
|
||||
|
||||
//Open a new connection, using the GET request on the URL endpoint
|
||||
fetch(qryString)
|
||||
.then((response) => response.json())
|
||||
.then((data_new) => {
|
||||
try {
|
||||
tb.build(data_new);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(err.stack);
|
||||
console.log(data_new);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
1
static/js/index_inline.min.js
vendored
1
static/js/index_inline.min.js
vendored
@ -1 +0,0 @@
|
||||
var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t","")),rows_list=[];buildHtmlTable("#bodyspot",payload_json,rows_list);var myRefresh=setInterval(myTimer,timer_interval_json);window.onload=function(){document.getElementById("form-filters").addEventListener("submit",mySearch)};
|
@ -1,17 +0,0 @@
|
||||
/* this is used to instance service worker for PWA
|
||||
* Check compatibility for the browser we're running this in
|
||||
*/
|
||||
if ("serviceWorker" in navigator) {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
console.log("[PWA Builder] active service worker found, no need to register");
|
||||
} else {
|
||||
// Register the service worker
|
||||
navigator.serviceWorker
|
||||
.register("service-worker.js", {
|
||||
scope: "./"
|
||||
})
|
||||
.then(function (reg) {
|
||||
console.log("[PWA Builder] Service worker has been registered for scope: " + reg.scope);
|
||||
});
|
||||
}
|
||||
}
|
4
static/js/load_css.min.js
vendored
4
static/js/load_css.min.js
vendored
@ -1,4 +0,0 @@
|
||||
/*
|
||||
loadCSS. [c]2020 Filament Group, Inc. MIT License */
|
||||
(function(l){var e=function(e,f,q,d){function m(a){if(b.body)return a();setTimeout(function(){m(a)})}function g(){a.addEventListener&&a.removeEventListener("load",g);a.media=q||"all"}var b=l.document,a=b.createElement("link");if(f)var c=f;else{var n=(b.body||b.getElementsByTagName("head")[0]).childNodes;c=n[n.length-1]}var p=b.styleSheets;if(d)for(var h in d)d.hasOwnProperty(h)&&a.setAttribute(h,d[h]);a.rel="stylesheet";a.href=e;a.media="only x";m(function(){c.parentNode.insertBefore(a,f?c:c.nextSibling)});
|
||||
var k=function(b){for(var d=a.href,c=p.length;c--;)if(p[c].href===d)return b();setTimeout(function(){k(b)})};a.addEventListener&&a.addEventListener("load",g);a.onloadcssdefined=k;k(g);return a};"undefined"!==typeof exports?exports.loadCSS=e:l.loadCSS=e})("undefined"!==typeof global?global:this);
|
@ -1,191 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build band_activity chart
|
||||
* ******************************************************************************/
|
||||
class band_activity {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {string} region This is the continent name (EU,AF,NA...) of the selected
|
||||
* @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF)
|
||||
* @param {Json} continents The continent list( EU, SA, ...)
|
||||
*/
|
||||
refresh(my_chart, end_point, region, bands, continents){
|
||||
// Asynchronous Data Loading
|
||||
fetch(end_point+'?continent='+region)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=Array.from(data["band activity"]).map(function (item) {
|
||||
return [item[1], item[0], item[2] || '-'];
|
||||
});
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
position: 'top',
|
||||
formatter: function (p) {
|
||||
var format = p.seriesName +' on ' + p.name +' band: '+'<strong>'+p.data[2]+'</strong>';
|
||||
return format;
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text:"Band activity",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
right: 'right',
|
||||
top : 'bottom',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
height: '80%',
|
||||
left: 25,
|
||||
top: 50,
|
||||
right: 60,
|
||||
bottom: 0,
|
||||
show: true,
|
||||
backgroundColor: 'rgb(255, 255, 255)',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: bands,
|
||||
axisTick: {
|
||||
show: true,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: continents,
|
||||
axisTick: {
|
||||
show: true,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
calculable: true,
|
||||
orient: 'vertical',
|
||||
right: 'right',
|
||||
top: 'center',
|
||||
min: 0,
|
||||
max: 30,
|
||||
inRange : {
|
||||
color: ['#ffffe6','yellow','red']
|
||||
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Spots',
|
||||
type: 'heatmap',
|
||||
data: dataMap,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(100, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} cont_cq The continent list( EU, SA, ...)
|
||||
* @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
constructor(chart_id, end_point, cont_cq, band_freq) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
|
||||
//populate continents array
|
||||
var continents=[];
|
||||
cont_cq.forEach(function myFunction(item, index) {
|
||||
continents[index]=item['id']
|
||||
});
|
||||
|
||||
//populate bands array
|
||||
var bands=[];
|
||||
band_freq.forEach(function myFunction(item, index) {
|
||||
bands[index]=item['id']
|
||||
});
|
||||
|
||||
//managing region
|
||||
var selectedContinent=getCookie("user_region");
|
||||
var selectedContinent_desc=getCookie("user_region_desc");
|
||||
if (!selectedContinent) {
|
||||
selectedContinent="EU";
|
||||
selectedContinent_desc="Europe";
|
||||
setCookie("user_region",selectedContinent,60);
|
||||
setCookie("user_region_desc",selectedContinent_desc,60);
|
||||
};
|
||||
|
||||
selectElement('continentInput', selectedContinent);
|
||||
|
||||
addEventHandler(document.getElementById('continentInput'), 'change', function() {
|
||||
selectedContinent=this.value;
|
||||
selectedContinent_desc=this.options[this.selectedIndex].text;
|
||||
setCookie("user_region",selectedContinent,60);
|
||||
setCookie("user_region_desc",selectedContinent_desc,60);
|
||||
plot_ba.refresh(myChart, end_point, selectedContinent,bands,continents);
|
||||
setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band');
|
||||
});
|
||||
|
||||
setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band');
|
||||
|
||||
this.refresh(myChart, end_point, selectedContinent,bands,continents);
|
||||
|
||||
/* resize chart*/
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_ba = new band_activity ('chart-band_activity','/plot_get_heatmap_data',continents_cq,band_frequencies);
|
||||
|
||||
/*setInterval(function(){doRefresh('chart-band_activity','/plot_get_heatmap_data')}, 5000);
|
||||
|
||||
function doRefresh(chart_id,end_point){
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,end_point);
|
||||
};
|
||||
|
||||
*/
|
||||
|
10
static/js/plot_band_activity.min.js
vendored
10
static/js/plot_band_activity.min.js
vendored
@ -1,10 +0,0 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,c,b){a!=Array.prototype&&a!=Object.prototype&&(a[c]=b.value)};
|
||||
$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var c=0;c<a.length;++c){var b=a[c];if(b&&b.Math==Math)return b}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.polyfill=function(a,c,b,d){if(c){b=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in b||(b[e]={});b=b[e]}a=a[a.length-1];d=b[a];c=c(d);c!=d&&null!=c&&$jscomp.defineProperty(b,a,{configurable:!0,writable:!0,value:c})}};
|
||||
$jscomp.polyfill("Array.from",function(a){return a?a:function(a,b,d){b=null!=b?b:function(a){return a};var c=[],f="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];if("function"==typeof f){a=f.call(a);for(var g=0;!(f=a.next()).done;)c.push(b.call(d,f.value,g++))}else for(f=a.length,g=0;g<f;g++)c.push(b.call(d,a[g],g));return c}},"es6","es3");
|
||||
$jscomp.findInternal=function(a,c,b){a instanceof String&&(a=String(a));for(var d=a.length,e=0;e<d;e++){var f=a[e];if(c.call(b,f,e,a))return{i:e,v:f}}return{i:-1,v:void 0}};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,b){return $jscomp.findInternal(this,a,b).v}},"es6","es3");
|
||||
var band_activity=function(a,c,b,d){var e=document.getElementById(a),f=echarts.init(e),g=[];b.forEach(function(a,b){g[b]=a.id});var l=[];d.forEach(function(a,b){l[b]=a.id});var h=getCookie("user_region"),k=getCookie("user_region_desc");h||(h="EU",k="Europe",setCookie("user_region",h,60),setCookie("user_region_desc",k,60));$("select").val(h).change();$("select").on("change",function(){h=this.value;k=$(this).find("option:selected").text();setCookie("user_region",h,60);setCookie("user_region_desc",k,
|
||||
60);plot_ba.refresh(f,c,h,l,g);$("#txt_continent").text(" Based on DX SPOTS from stations in "+k+" during the last 15 minutes, displayed by Continent and Band")});$("#txt_continent").text(" Based on DX SPOTS from stations in "+k+" during the last 15 minutes, displayed by Continent and Band");this.refresh(f,c,h,l,g);var m=echarts.init(document.querySelector("#"+a),null);window.addEventListener("resize",function(){m.resize()})};
|
||||
band_activity.prototype.refresh=function(a,c,b,d,e){fetch(c+"?continent="+b).then(function(a){return a.json()}).then(function(b){var c=get_last_refresh(b);b=Array.from(b["band activity"]).map(function(a){return[a[1],a[0],a[2]||"-"]});a.setOption({tooltip:{position:"top",formatter:function(a){return a.seriesName+" on "+a.name+" band: <strong>"+a.data[2]+"</strong>"}},title:{text:"Band activity",subtext:c,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,orient:"vertical",right:"right",top:"bottom",
|
||||
feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},grid:{height:"80%",left:25,top:50,right:60,bottom:0,show:!0,backgroundColor:"rgb(255, 255, 255)"},xAxis:{type:"category",data:d,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},yAxis:{type:"category",data:e,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},visualMap:{calculable:!0,orient:"vertical",right:"right",top:"center",min:0,max:30,inRange:{color:["#ffffe6","yellow","red"]}},series:[{name:"Spots",
|
||||
type:"heatmap",data:b,label:{show:!1},emphasis:{itemStyle:{shadowBlur:10,shadowColor:"rgba(100, 0, 0, 0.5)"}}}]})})};var plot_ba=new band_activity("chart-band_activity","/plot_get_heatmap_data",continents_cq,band_frequencies);
|
@ -1,119 +0,0 @@
|
||||
|
||||
/**
|
||||
* Create a cookie
|
||||
*
|
||||
* @param {String} cname cookie name
|
||||
* @param {string} cvalue cookie value
|
||||
* @param {integer} exdays the number of the days for cookie expiration
|
||||
*/
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (exdays*24*60*60*1000));
|
||||
let expires = "expires="+ d.toUTCString();
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/" + ";Samesite=Strict;Secure=True";
|
||||
};
|
||||
|
||||
/**
|
||||
* get a cookie
|
||||
*
|
||||
* @param {String} cname cookie name
|
||||
* @returns {string} cookie value
|
||||
*/
|
||||
function getCookie(cname) {
|
||||
let name = cname + "=";
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
for(let i = 0; i <ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param {String} date date of the last refresh
|
||||
* @returns {string} string formatted
|
||||
*/
|
||||
function get_last_refresh(data){
|
||||
var dt_refresh = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
||||
var dt_refresh;
|
||||
dt_refresh.setUTCSeconds(data["last_refresh"]);
|
||||
const pad = (n,s=2) => (`${new Array(s).fill(0)}${n}`).slice(-s);
|
||||
var hours = pad (dt_refresh.getHours());
|
||||
var minutes = pad(dt_refresh.getMinutes());
|
||||
var months_names = get_months_names();
|
||||
var month = months_names[dt_refresh.getMonth()];
|
||||
var day = dt_refresh.getDate();
|
||||
var year = dt_refresh.getFullYear();
|
||||
var last_refresh = "Data refresh: "+day+" of "+month+" "+year+" at "+hours+":"+minutes;
|
||||
return last_refresh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @returns {Array} list of months as short names
|
||||
*/
|
||||
function get_months_names() {
|
||||
var months_name=[];
|
||||
for (let monthNumber = 1; monthNumber < 13; monthNumber++) {
|
||||
const date = new Date();
|
||||
date.setMonth(monthNumber - 1);
|
||||
months_name.push(date.toLocaleString('en-US', { month: 'short' }));
|
||||
};
|
||||
return months_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param {Integer} num Number to be formatted
|
||||
* @returns {string} Number formatted with K for thousands and M for millions
|
||||
*/
|
||||
function format_u_k_m(num) {
|
||||
let label;
|
||||
let sign=1;
|
||||
if (num<0){
|
||||
sign=-1;
|
||||
}
|
||||
let abs_num=Math.abs(num);
|
||||
if (abs_num==0) {
|
||||
label=abs_num
|
||||
} else {
|
||||
if (abs_num>0 && abs_num <1000) {
|
||||
label=abs_num*sign;
|
||||
} else {
|
||||
if (abs_num>=1000 && abs_num < 1000000) {
|
||||
label=abs_num/1000*sign+"K";
|
||||
} else {
|
||||
if (abs_num>=1000000) {
|
||||
label=abs_num/1000000*sign+"M";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
/*
|
||||
function doRefresh(){
|
||||
|
||||
var chartDom = document.getElementById('chart-dx_spots_trend');
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,'/plot_get_dx_spots_trend');
|
||||
|
||||
};
|
||||
|
||||
setInterval(function(){doRefresh()}, 5000);
|
||||
|
||||
*/
|
6
static/js/plot_common.min.js
vendored
6
static/js/plot_common.min.js
vendored
@ -1,6 +0,0 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.createTemplateTagFirstArg=function(a){return a.raw=a};$jscomp.createTemplateTagFirstArgWithRaw=function(a,b){a.raw=b;return a};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};
|
||||
$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};$jscomp.polyfill("Array.prototype.fill",function(a){return a?a:function(a,c,d){var b=this.length||0;0>c&&(c=Math.max(0,b+c));if(null==d||d>b)d=b;d=Number(d);0>d&&(d=Math.max(0,b+d));for(c=Number(c||0);c<d;c++)this[c]=a;return this}},"es6","es3");
|
||||
function setCookie(a,b,c){var d=new Date;d.setTime(d.getTime()+864E5*c);c="expires="+d.toUTCString();document.cookie=a+"="+b+";"+c+";path=/;Samesite=Strict;Secure=True"}function getCookie(a){a+="=";for(var b=decodeURIComponent(document.cookie).split(";"),c=0;c<b.length;c++){for(var d=b[c];" "==d.charAt(0);)d=d.substring(1);if(0==d.indexOf(a))return d.substring(a.length,d.length)}return""}
|
||||
function get_last_refresh(a){var b=new Date(0);b.setUTCSeconds(a.last_refresh);var c=function(a,b){b=void 0===b?2:b;return(""+Array(b).fill(0)+a).slice(-b)};a=c(b.getHours());c=c(b.getMinutes());var d=get_months_names()[b.getMonth()],e=b.getDate();b=b.getFullYear();return"Data refresh: "+e+" of "+d+" "+b+" at "+a+":"+c}function get_months_names(){for(var a=[],b=1;13>b;b++){var c=new Date;c.setMonth(b-1);a.push(c.toLocaleString("en-US",{month:"short"}))}return a}
|
||||
function format_u_k_m(a){var b,c=1;0>a&&(c=-1);a=Math.abs(a);0==a?b=a:0<a&&1E3>a?b=a*c:1E3<=a&&1E6>a?b=a/1E3*c+"K":1E6<=a&&(b=a/1E6*c+"M");return b};
|
@ -1,142 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build dx spots per month chart
|
||||
* ******************************************************************************/
|
||||
|
||||
class dx_spots_per_month {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
//$.getJSON(end_point).done(function(data) {
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var year_now = new Date().getFullYear();
|
||||
var year_0 = (year_now - 0).toString();
|
||||
var year_1 = (year_now - 1).toString();
|
||||
var year_2 = (year_now - 2).toString();
|
||||
|
||||
var months_name = get_months_names();
|
||||
|
||||
var dataMap_year_0=[];
|
||||
var dataMap_year_1=[];
|
||||
var dataMap_year_2=[];
|
||||
for (let i = 1; i < 13; i++) {
|
||||
dataMap_year_0.push(data.spots_per_month[i].year_0);
|
||||
dataMap_year_1.push(data.spots_per_month[i].year_1);
|
||||
dataMap_year_2.push(data.spots_per_month[i].year_2);
|
||||
}
|
||||
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: "DX SPOTS per month",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
legend: {
|
||||
data: [year_2, year_1, year_0],
|
||||
bottom: "bottom"
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar', 'stack'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: months_name
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (function (value){
|
||||
return format_u_k_m(value)
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: year_2,
|
||||
type: 'bar',
|
||||
barGap: 0,
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_2
|
||||
},
|
||||
{
|
||||
name: year_1,
|
||||
type: 'bar',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_1
|
||||
|
||||
},
|
||||
{
|
||||
name: year_0,
|
||||
type: 'bar',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_0
|
||||
},
|
||||
]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_dspm = new dx_spots_per_month ('chart-dx_spots_x_month','/plot_get_dx_spots_per_month');
|
4
static/js/plot_dx_spots_per_month.min.js
vendored
4
static/js/plot_dx_spots_per_month.min.js
vendored
@ -1,4 +0,0 @@
|
||||
var dx_spots_per_month=function(e,c){var a=document.getElementById(e);a=echarts.init(a);this.refresh(a,c);var m=echarts.init(document.querySelector("#"+e),null);window.addEventListener("resize",function(){m.resize()})};
|
||||
dx_spots_per_month.prototype.refresh=function(e,c){fetch(c).then(function(a){return a.json()}).then(function(a){var c=get_last_refresh(a),b=(new Date).getFullYear(),f=(b-0).toString(),g=(b-1).toString();b=(b-2).toString();for(var n=get_months_names(),h=[],k=[],l=[],d=1;13>d;d++)h.push(a.spots_per_month[d].year_0),k.push(a.spots_per_month[d].year_1),l.push(a.spots_per_month[d].year_2);e.setOption({tooltip:{trigger:"axis",axisPointer:{type:"shadow"}},title:{text:"DX SPOTS per month",subtext:c,top:"top",
|
||||
left:"left"},legend:{data:[b,g,f],bottom:"bottom"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},magicType:{show:!0,type:["line","bar","stack"]},restore:{show:!0},saveAsImage:{show:!0}}},xAxis:[{type:"category",axisTick:{show:!1},data:n}],yAxis:[{type:"value",axisLabel:{formatter:function(a){return format_u_k_m(a)}}}],series:[{name:b,type:"bar",barGap:0,emphasis:{focus:"series"},data:l},{name:g,type:"bar",emphasis:{focus:"series"},
|
||||
data:k},{name:f,type:"bar",emphasis:{focus:"series"},data:h}]})})};var plot_dspm=new dx_spots_per_month("chart-dx_spots_x_month","/plot_get_dx_spots_per_month");
|
@ -1,136 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build dx spots trend
|
||||
* ******************************************************************************/
|
||||
|
||||
class dx_spots_trend {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
//$.getJSON(end_point).done(function(data) {
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=[];
|
||||
for (const [key, value] of Object.entries(data["spots_trend"])) {
|
||||
var tuple=[];
|
||||
tuple.push(key);
|
||||
tuple.push(value);
|
||||
dataMap.push(tuple);
|
||||
}
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
position: function (pt) {
|
||||
return [pt[0], '10%'];
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: "DX SPOTS trend",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
saveAsImage: {},
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '10%'],
|
||||
axisLabel: {
|
||||
formatter: (function (value){
|
||||
return format_u_k_m(value)
|
||||
})
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 65,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 20
|
||||
},
|
||||
],
|
||||
|
||||
series: [
|
||||
{
|
||||
name: 'Spots',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
itemStyle: {
|
||||
color: '#078513'
|
||||
},
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#57fa75'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#118226'
|
||||
}
|
||||
])
|
||||
},
|
||||
data: dataMap
|
||||
}
|
||||
]
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_dst = new dx_spots_trend ('chart-dx_spots_trend','/plot_get_dx_spots_trend');
|
8
static/js/plot_dx_spots_trend.min.js
vendored
8
static/js/plot_dx_spots_trend.min.js
vendored
@ -1,8 +0,0 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(a){var b=0;return function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}}};$jscomp.arrayIterator=function(a){return{next:$jscomp.arrayIteratorImpl(a)}};$jscomp.makeIterator=function(a){var b="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];return b?b.call(a):$jscomp.arrayIterator(a)};$jscomp.owns=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};$jscomp.ASSUME_ES5=!1;
|
||||
$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};
|
||||
$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};$jscomp.polyfill("Object.entries",function(a){return a?a:function(a){var c=[],b;for(b in a)$jscomp.owns(a,b)&&c.push([b,a[b]]);return c}},"es8","es3");
|
||||
var dx_spots_trend=function(a,b){var c=document.getElementById(a);c=echarts.init(c);this.refresh(c,b);var d=echarts.init(document.querySelector("#"+a),null);window.addEventListener("resize",function(){d.resize()})};
|
||||
dx_spots_trend.prototype.refresh=function(a,b){fetch(b).then(function(a){return a.json()}).then(function(b){var c=get_last_refresh(b),e=[];b=$jscomp.makeIterator(Object.entries(b.spots_trend));for(var f=b.next();!f.done;f=b.next()){var g=$jscomp.makeIterator(f.value);f=g.next().value;g=g.next().value;var h=[];h.push(f);h.push(g);e.push(h)}a.setOption({tooltip:{trigger:"axis",position:function(a){return[a[0],"10%"]}},title:{text:"DX SPOTS trend",subtext:c,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,
|
||||
orient:"vertical",left:"right",top:"center",feature:{dataView:{show:!0,readOnly:!1},dataZoom:{yAxisIndex:"none"},restore:{},magicType:{show:!0,type:["line","bar"]},saveAsImage:{}}},xAxis:{type:"time",boundaryGap:!1},yAxis:{type:"value",boundaryGap:[0,"10%"],axisLabel:{formatter:function(a){return format_u_k_m(a)}}},dataZoom:[{type:"inside",start:65,end:100},{start:0,end:20}],series:[{name:"Spots",type:"line",smooth:!0,symbol:"none",itemStyle:{color:"#078513"},areaStyle:{color:new echarts.graphic.LinearGradient(0,
|
||||
0,0,1,[{offset:0,color:"#57fa75"},{offset:1,color:"#118226"}])},data:e}]})})};var plot_dst=new dx_spots_trend("chart-dx_spots_trend","/plot_get_dx_spots_trend");
|
@ -1,152 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build band/hour chart
|
||||
* ******************************************************************************/
|
||||
|
||||
class hour_band {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
refresh(my_chart,end_point,bands) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the dat
|
||||
var last_refresh=get_last_refresh(data);
|
||||
//set hour indicator names
|
||||
var hour_indicators=[];
|
||||
|
||||
//for (let i = 0; i < 24; i++) {
|
||||
for (let i = 23; i > -1; i--) {
|
||||
var hour_name={};
|
||||
var s=i.toString();
|
||||
hour_name["name"]=s;
|
||||
hour_indicators.push(hour_name);
|
||||
}
|
||||
|
||||
//cycling whithin each bands and hours
|
||||
var dataMap=[];
|
||||
bands.forEach(band_item => {
|
||||
var qso=[];
|
||||
|
||||
//for (let i = 0; i < 24; i++) {
|
||||
for (let i = 23; i > -1; i--) {
|
||||
try {
|
||||
var value=data["hour_band"][band_item][i];
|
||||
if (typeof value == 'undefined') {
|
||||
value = 0;
|
||||
}
|
||||
qso.push(value);
|
||||
} catch (TypeError) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
var tot={"value":qso,"name":band_item};
|
||||
dataMap.push(tot);
|
||||
});
|
||||
|
||||
|
||||
//options
|
||||
my_chart.setOption({
|
||||
legend: {
|
||||
|
||||
orient: 'horizontal',
|
||||
left: 'left',
|
||||
bottom: 'bottom'
|
||||
},
|
||||
title: {
|
||||
text: "DX SPOTS per hour in last month",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
right: 'right',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
radar: {
|
||||
shape: 'circle',
|
||||
//startAngle: 285, //0 on left side
|
||||
startAngle: 105, //0 on top
|
||||
indicator: hour_indicators,
|
||||
center: ['47%', '46%'],
|
||||
axisName: {
|
||||
color: 'rgb(80,80,80)',
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
type: 'radar',
|
||||
symbol: 'none',
|
||||
data: dataMap,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params) => {
|
||||
return "Band: "+params.name;
|
||||
},
|
||||
},
|
||||
emphasis: {
|
||||
lineStyle: {
|
||||
width: 4
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
constructor(chart_id,end_point,band_freq) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
|
||||
//populate bands array
|
||||
var bands=[];
|
||||
band_freq.forEach(function myFunction(item, index) {
|
||||
bands[index]=item['id']
|
||||
});
|
||||
|
||||
this.refresh(myChart,end_point,bands);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_hb = new hour_band('chart-hour_band','/plot_get_hour_band',band_frequencies);
|
4
static/js/plot_hour_band.min.js
vendored
4
static/js/plot_hour_band.min.js
vendored
@ -1,4 +0,0 @@
|
||||
var hour_band=function(c,a,f){var b=document.getElementById(c);b=echarts.init(b);var d=[];f.forEach(function(b,c){d[c]=b.id});this.refresh(b,a,d);var e=echarts.init(document.querySelector("#"+c),null);window.addEventListener("resize",function(){e.resize()})};
|
||||
hour_band.prototype.refresh=function(c,a,f){fetch(a).then(function(b){return b.json()}).then(function(b){for(var d=get_last_refresh(b),e=[],a=23;-1<a;a--){var g={};g.name=a.toString();e.push(g)}var h=[];f.forEach(function(a){for(var c=[],d=23;-1<d;d--)try{var e=b.hour_band[a][d];"undefined"==typeof e&&(e=0);c.push(e)}catch(k){}h.push({value:c,name:a})});c.setOption({legend:{orient:"horizontal",left:"left",bottom:"bottom"},title:{text:"DX SPOTS per hour in last month",subtext:d,top:"top",right:"right"},
|
||||
tooltip:{trigger:"axis"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},radar:{shape:"circle",startAngle:105,indicator:e,center:["47%","46%"],axisName:{color:"rgb(80,80,80)"}},series:[{lineStyle:{width:2},type:"radar",symbol:"none",data:h,tooltip:{trigger:"item",formatter:function(a){return"Band: "+a.name}},emphasis:{lineStyle:{width:4}}}]})})};
|
||||
var plot_hb=new hour_band("chart-hour_band","/plot_get_hour_band",band_frequencies);
|
@ -1,163 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build world dx spots live
|
||||
* ******************************************************************************/
|
||||
|
||||
class world_dx_spots_live {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
fetch('world.json')
|
||||
.then(response => response.text())
|
||||
.then(geoJson => {
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=[];
|
||||
data["world_dx_spots_live"].forEach(function myFunction(item, index) {
|
||||
//lon, lat, number of qso
|
||||
dataMap.push({"value":[item["lat"],item["lon"],item["count"]]});
|
||||
});
|
||||
|
||||
my_chart.hideLoading();
|
||||
echarts.registerMap('WR', geoJson);
|
||||
|
||||
my_chart.setOption( {
|
||||
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 50,
|
||||
inRange: {
|
||||
symbolSize: [5, 20]
|
||||
}
|
||||
},
|
||||
|
||||
geo: {
|
||||
type: 'map',
|
||||
map: 'WR',
|
||||
roam: true,
|
||||
zoom: 1.2,
|
||||
aspectScale: 0.70,
|
||||
layoutCenter: ['50%', '54%'],
|
||||
layoutSize: '100%',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#323c48',
|
||||
borderColor: '#111'
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#2a333d'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function(val) {
|
||||
var out="lat: "+val.value[0]+
|
||||
" lon: "+val.value[1]+"</BR>"+
|
||||
"Spots: <STRONG>"+ val.value[2] +"</STRONG></BR>";
|
||||
return out;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
iconStyle: {
|
||||
borderColor: '#fff',
|
||||
},
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
//backgroundColor: '#404a59',
|
||||
backgroundColor: '#596475',
|
||||
title: {
|
||||
text: "World DX SPOTS in last hour",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
right:'right',
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
subtextStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
data:dataMap,
|
||||
label: {
|
||||
emphasis: {
|
||||
position: 'right',
|
||||
show: false
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#eea638'
|
||||
}
|
||||
},
|
||||
/*
|
||||
symbolSize: function (val) {
|
||||
return val[2] / 4;
|
||||
},
|
||||
*/
|
||||
}
|
||||
]
|
||||
|
||||
}) //end options
|
||||
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_wdsl = new world_dx_spots_live ('chart-world_dx_spots_live','/plot_get_world_dx_spots_live');
|
4
static/js/plot_world_dx_spots_live.min.js
vendored
4
static/js/plot_world_dx_spots_live.min.js
vendored
@ -1,4 +0,0 @@
|
||||
var world_dx_spots_live=function(b,c){var a=document.getElementById(b);a=echarts.init(a);this.refresh(a,c);var d=echarts.init(document.querySelector("#"+b),null);window.addEventListener("resize",function(){d.resize()})};
|
||||
world_dx_spots_live.prototype.refresh=function(b,c){fetch(c).then(function(a){return a.json()}).then(function(a){fetch("world.json").then(function(a){return a.text()}).then(function(c){var d=get_last_refresh(a),e=[];a.world_dx_spots_live.forEach(function(a,b){e.push({value:[a.lat,a.lon,a.count]})});b.hideLoading();echarts.registerMap("WR",c);b.setOption({visualMap:{show:!1,min:0,max:50,inRange:{symbolSize:[5,20]}},geo:{type:"map",map:"WR",roam:!0,zoom:1.2,aspectScale:.7,layoutCenter:["50%","54%"],
|
||||
layoutSize:"100%",itemStyle:{normal:{areaColor:"#323c48",borderColor:"#111"},emphasis:{areaColor:"#2a333d"}},label:{emphasis:{show:!1}}},tooltip:{trigger:"item",formatter:function(a){return"lat: "+a.value[0]+" lon: "+a.value[1]+"</BR>Spots: <STRONG>"+a.value[2]+"</STRONG></BR>"}},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",iconStyle:{borderColor:"#fff"},feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},restore:{show:!0},saveAsImage:{show:!0}}},legend:{show:!1},backgroundColor:"#596475",
|
||||
title:{text:"World DX SPOTS in last hour",subtext:d,top:"top",right:"right",textStyle:{color:"#fff"},subtextStyle:{color:"#fff"}},series:[{type:"scatter",coordinateSystem:"geo",data:e,label:{emphasis:{position:"right",show:!1}},itemStyle:{normal:{color:"#eea638"}}}]})})})};var plot_wdsl=new world_dx_spots_live("chart-world_dx_spots_live","/plot_get_world_dx_spots_live");
|
2
static/js/rel/README.md
Normal file
2
static/js/rel/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This folder `static/js/rel/` contains the javascript modules for **release** environment.
|
||||
You **CANNOT change** these scripts. Make changes only in `static/js/dev/` folder
|
1
static/js/rel/callsign_inline.min.js
vendored
Normal file
1
static/js/rel/callsign_inline.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t","")),qryString="spotlist?c="+my_callsign;fetch(qryString).then(l=>l.json()).then(o=>{try{tb.build(o,my_callsign)}catch(l){console.log(l),console.log(l.stack),console.log(o)}});
|
1
static/js/rel/callsign_search.min.js
vendored
Normal file
1
static/js/rel/callsign_search.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
function myCallsignSearch(){0<(callsign=document.getElementById("callsignInput").value).replace(/\s/g,"").length&&(location.href="/callsign.html?c=".concat(callsign.trim().toUpperCase()))}
|
1
static/js/rel/clock.min.js
vendored
Normal file
1
static/js/rel/clock.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
function showTime(){var e=new Date;let t=new Date(e.getTime()+6e4*e.getTimezoneOffset()).toTimeString().split(" ")[0];t=t.split(":")[0]+":"+t.split(":")[1],document.getElementById("MyClockDisplay").innerText=t,document.getElementById("MyClockDisplay").textContent=t,setTimeout(showTime,1e3)}showTime();
|
1
static/js/rel/common.min.js
vendored
Normal file
1
static/js/rel/common.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
function setCookie(e,t,n){var r=new Date,n=(r.setTime(r.getTime()+24*n*60*60*1e3),"expires="+r.toUTCString());document.cookie=e+"="+t+";"+n+";path=/;Samesite=Strict;Secure=True"}function getCookie(e){var n=e+"=",r=decodeURIComponent(document.cookie).split(";");for(let t=0;t<r.length;t++){let e=r[t];for(;" "==e.charAt(0);)e=e.substring(1);if(0==e.indexOf(n))return e.substring(n.length,e.length)}return""}function get_last_refresh(e){var t=new Date(0),e=(t.setUTCSeconds(e.last_refresh),(e,t=2)=>(""+new Array(t).fill(0)+e).slice(-t)),n=e(t.getHours()),e=e(t.getMinutes()),r=get_months_names()[t.getMonth()];return"Data refresh: "+t.getDate()+" of "+r+" "+t.getFullYear()+" at "+n+":"+e}function get_months_names(){var t=[];for(let e=1;e<13;e++){var n=new Date;n.setMonth(e-1),t.push(n.toLocaleString("en-US",{month:"short"}))}return t}function format_u_k_m(e){let t,n=1;e<0&&(n=-1);e=Math.abs(e);return 0==e?t=e:0<e&&e<1e3?t=e*n:1e3<=e&&e<1e6?t=e/1e3*n+"K":1e6<=e&&(t=e/1e6*n+"M"),t}function selectElement(e,t){document.getElementById(e).value=t}function addEventHandler(e,t,n){e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent&&e.attachEvent("on"+t,n)}function setText(e,t){document.getElementById(e).innerHTML=t}
|
1
static/js/rel/cookie_consent.min.js
vendored
Normal file
1
static/js/rel/cookie_consent.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
let cookie_modal=new bootstrap.Modal(document.getElementById("cookie_consent_modal"),{keyboard:!1});cookie_modal.show(),document.getElementById("cookie_consent_btn").onclick=function(){setCookie("cookie_consent",!0,30),cookie_modal.hide()};
|
1
static/js/rel/index_inline.min.js
vendored
Normal file
1
static/js/rel/index_inline.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t","")),myRefresh=(refresh_timer(),setInterval(refresh_timer,timer_interval_json));window.onload=()=>{document.getElementById("form-filters").addEventListener("submit",mySearch)};
|
@ -1 +1 @@
|
||||
"serviceWorker"in navigator&&(navigator.serviceWorker.controller?console.log("[PWA Builder] active service worker found, no need to register"):navigator.serviceWorker.register("service-worker.js",{scope:"./"}).then(function(a){console.log("[PWA Builder] Service worker has been registered for scope: "+a.scope)}));
|
||||
"serviceWorker"in navigator&&(navigator.serviceWorker.controller?console.log("[PWA Builder] active service worker found, no need to register"):navigator.serviceWorker.register("service-worker.js",{scope:"./"}).then(function(e){console.log("[PWA Builder] Service worker has been registered for scope: "+e.scope)}));
|
1
static/js/rel/load_css.min.js
vendored
Normal file
1
static/js/rel/load_css.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(c){"use strict";function e(e,t,n,o){var i,r,d=c.document,a=d.createElement("link"),f=(r=t||(i=(d.body||d.getElementsByTagName("head")[0]).childNodes)[i.length-1],d.styleSheets);if(o)for(var l in o)o.hasOwnProperty(l)&&a.setAttribute(l,o[l]);function s(e){for(var t=a.href,n=f.length;n--;)if(f[n].href===t)return e();setTimeout(function(){s(e)})}function u(){a.addEventListener&&a.removeEventListener("load",u),a.media=n||"all"}return a.rel="stylesheet",a.href=e,a.media="only x",function e(t){if(d.body)return t();setTimeout(function(){e(t)})}(function(){r.parentNode.insertBefore(a,t?r:r.nextSibling)}),a.addEventListener&&a.addEventListener("load",u),a.onloadcssdefined=s,s(u),a}"undefined"!=typeof exports?exports.loadCSS=e:c.loadCSS=e}("undefined"!=typeof global?global:this);
|
1
static/js/rel/plot_band_activity.min.js
vendored
Normal file
1
static/js/rel/plot_band_activity.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class band_activity{refresh(n,t,e,o,i){fetch(t+"?continent="+e).then(t=>t.json()).then(t=>{var e=get_last_refresh(t),t=Array.from(t["band activity"]).map(function(t){return[t[1],t[0],t[2]||"-"]});n.setOption({tooltip:{position:"top",formatter:function(t){return t.seriesName+" on "+t.name+" band: <strong>"+t.data[2]+"</strong>"}},title:{text:"Band activity",subtext:e,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,orient:"vertical",right:"right",top:"bottom",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},grid:{height:"80%",left:25,top:50,right:60,bottom:0,show:!0,backgroundColor:"rgb(255, 255, 255)"},xAxis:{type:"category",data:o,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},yAxis:{type:"category",data:i,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},visualMap:{calculable:!0,orient:"vertical",right:"right",top:"center",min:0,max:30,inRange:{color:["#ffffe6","yellow","red"]}},series:[{name:"Spots",type:"heatmap",data:t,label:{show:!1},emphasis:{itemStyle:{shadowBlur:10,shadowColor:"rgba(100, 0, 0, 0.5)"}}}]})})}constructor(t,e,n,o){var i=document.getElementById(t),s=echarts.init(i),a=[],r=(n.forEach(function(t,e){a[e]=t.id}),[]),c=(o.forEach(function(t,e){r[e]=t.id}),getCookie("user_region")),d=getCookie("user_region_desc"),h=(c||(c="EU",d="Europe",setCookie("user_region",c,60),setCookie("user_region_desc",d,60)),selectElement("continentInput",c),addEventHandler(document.getElementById("continentInput"),"change",function(){c=this.value,d=this.options[this.selectedIndex].text,setCookie("user_region",c,60),setCookie("user_region_desc",d,60),plot_ba.refresh(s,e,c,r,a),setText("txt_continent"," Based on DX SPOTS from stations in "+d+" during the last 15 minutes, displayed by Continent and Band")}),setText("txt_continent"," Based on DX SPOTS from stations in "+d+" during the last 15 minutes, displayed by Continent and Band"),this.refresh(s,e,c,r,a),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){h.resize()})}}let plot_ba=new band_activity("chart-band_activity","/plot_get_heatmap_data",continents_cq,band_frequencies);
|
1
static/js/rel/plot_dx_spots_per_month.min.js
vendored
Normal file
1
static/js/rel/plot_dx_spots_per_month.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class dx_spots_per_month{refresh(h,t){fetch(t).then(t=>t.json()).then(e=>{var t=get_last_refresh(e),s=(new Date).getFullYear(),o=(+s).toString(),r=(s-1).toString(),s=(s-2).toString(),a=get_months_names(),n=[],i=[],p=[];for(let t=1;t<13;t++)n.push(e.spots_per_month[t].year_0),i.push(e.spots_per_month[t].year_1),p.push(e.spots_per_month[t].year_2);h.setOption({tooltip:{trigger:"axis",axisPointer:{type:"shadow"}},title:{text:"DX SPOTS per month",subtext:t,top:"top",left:"left"},legend:{data:[s,r,o],bottom:"bottom"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},magicType:{show:!0,type:["line","bar","stack"]},restore:{show:!0},saveAsImage:{show:!0}}},xAxis:[{type:"category",axisTick:{show:!1},data:a}],yAxis:[{type:"value",axisLabel:{formatter:function(t){return format_u_k_m(t)}}}],series:[{name:s,type:"bar",barGap:0,emphasis:{focus:"series"},data:p},{name:r,type:"bar",emphasis:{focus:"series"},data:i},{name:o,type:"bar",emphasis:{focus:"series"},data:n}]})})}constructor(t,e){var s=document.getElementById(t),s=echarts.init(s),o=(this.refresh(s,e),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){o.resize()})}}let plot_dspm=new dx_spots_per_month("chart-dx_spots_x_month","/plot_get_dx_spots_per_month");
|
1
static/js/rel/plot_dx_spots_trend.min.js
vendored
Normal file
1
static/js/rel/plot_dx_spots_trend.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class dx_spots_trend{refresh(a,t){fetch(t).then(t=>t.json()).then(t=>{var e,o,r=get_last_refresh(t),s=[];for([e,o]of Object.entries(t.spots_trend)){var n=[];n.push(e),n.push(o),s.push(n)}a.setOption({tooltip:{trigger:"axis",position:function(t){return[t[0],"10%"]}},title:{text:"DX SPOTS trend",subtext:r,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{dataView:{show:!0,readOnly:!1},dataZoom:{yAxisIndex:"none"},restore:{},magicType:{show:!0,type:["line","bar"]},saveAsImage:{}}},xAxis:{type:"time",boundaryGap:!1},yAxis:{type:"value",boundaryGap:[0,"10%"],axisLabel:{formatter:function(t){return format_u_k_m(t)}}},dataZoom:[{type:"inside",start:65,end:100},{start:0,end:20}],series:[{name:"Spots",type:"line",smooth:!0,symbol:"none",itemStyle:{color:"#078513"},areaStyle:{color:new echarts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:"#57fa75"},{offset:1,color:"#118226"}])},data:s}]})})}constructor(t,e){var o=document.getElementById(t),o=echarts.init(o),r=(this.refresh(o,e),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){r.resize()})}}let plot_dst=new dx_spots_trend("chart-dx_spots_trend","/plot_get_dx_spots_trend");
|
1
static/js/rel/plot_hour_band.min.js
vendored
Normal file
1
static/js/rel/plot_hour_band.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class hour_band{refresh(i,t,s){fetch(t).then(t=>t.json()).then(a=>{var t=get_last_refresh(a),e=[];for(let t=23;-1<t;t--){var r={},o=t.toString();r.name=o,e.push(r)}var n=[];s.forEach(e=>{var r=[];for(let t=23;-1<t;t--)try{var o=a.hour_band[e][t];r.push(o=void 0===o?0:o)}catch(t){}n.push({value:r,name:e})}),i.setOption({legend:{orient:"horizontal",left:"left",bottom:"bottom"},title:{text:"DX SPOTS per hour in last month",subtext:t,top:"top",right:"right"},tooltip:{trigger:"axis"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},radar:{shape:"circle",startAngle:105,indicator:e,center:["47%","46%"],axisName:{color:"rgb(80,80,80)"}},series:[{lineStyle:{width:2},type:"radar",symbol:"none",data:n,tooltip:{trigger:"item",formatter:t=>"Band: "+t.name},emphasis:{lineStyle:{width:4}}}]})})}constructor(t,e,r){var o=document.getElementById(t),o=echarts.init(o),a=[],n=(r.forEach(function(t,e){a[e]=t.id}),this.refresh(o,e,a),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){n.resize()})}}let plot_hb=new hour_band("chart-hour_band","/plot_get_hour_band",band_frequencies);
|
1
static/js/rel/plot_world_dx_spots_live.min.js
vendored
Normal file
1
static/js/rel/plot_world_dx_spots_live.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class world_dx_spots_live{refresh(s,e){fetch(e).then(e=>e.json()).then(r=>{fetch("world.json").then(e=>e.text()).then(e=>{var t=get_last_refresh(r),o=[];r.world_dx_spots_live.forEach(function(e,t){o.push({value:[e.lat,e.lon,e.count]})}),s.hideLoading(),echarts.registerMap("WR",e),s.setOption({visualMap:{show:!1,min:0,max:50,inRange:{symbolSize:[5,20]}},geo:{type:"map",map:"WR",roam:!0,zoom:1.2,aspectScale:.7,layoutCenter:["50%","54%"],layoutSize:"100%",itemStyle:{normal:{areaColor:"#323c48",borderColor:"#111"},emphasis:{areaColor:"#2a333d"}},label:{emphasis:{show:!1}}},tooltip:{trigger:"item",formatter:function(e){return"Spots: <STRONG>"+e.value[2]+"</STRONG>"}},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",iconStyle:{borderColor:"#fff"},feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},restore:{show:!0},saveAsImage:{show:!0}}},legend:{show:!1},backgroundColor:"#596475",title:{text:"World DX SPOTS in last hour",subtext:t,top:"top",right:"right",textStyle:{color:"#fff"},subtextStyle:{color:"#fff"}},series:[{type:"scatter",coordinateSystem:"geo",data:o,label:{emphasis:{position:"right",show:!1}},itemStyle:{normal:{color:"#eea638"}}}]})})})}constructor(e,t){var o=document.getElementById(e),o=echarts.init(o),r=(this.refresh(o,t),echarts.init(document.querySelector("#"+e),null));window.addEventListener("resize",function(){r.resize()})}}let plot_wdsl=new world_dx_spots_live("chart-world_dx_spots_live","/plot_get_world_dx_spots_live");
|
1
static/js/rel/table.min.js
vendored
Normal file
1
static/js/rel/table.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,353 +0,0 @@
|
||||
class table_builder {
|
||||
|
||||
/**
|
||||
* Table builder constructor
|
||||
* @param selector {string} The html identifier where build the spots table
|
||||
*/
|
||||
constructor(selector) {
|
||||
this.selector=selector;
|
||||
this.current_data=[];
|
||||
}
|
||||
/**
|
||||
* @return last_rowid {integer} the last rowid
|
||||
*/
|
||||
getLastRowId() {
|
||||
let last_rowid;
|
||||
if (this.current_data == null) {
|
||||
last_rowid=0;
|
||||
} else {
|
||||
if (this.current_data.length < 1) {
|
||||
last_rowid=0;
|
||||
} else {
|
||||
last_rowid=this.current_data[0].rowid;
|
||||
}
|
||||
}
|
||||
return last_rowid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line {object} with the data of a single spot
|
||||
* @param isnew {boolean} is the new rows indicator
|
||||
* @param dt_current {string} current date in dd/mm/yyyy format
|
||||
* @param callsign {string} optional callsign
|
||||
* @return a <tr> dom element
|
||||
*/
|
||||
#buildRow(line, isnew, dt_current,callsign=""){
|
||||
const row = document.createElement("tr");
|
||||
row.id= line.rowid;
|
||||
|
||||
if (callsign.length>0) {
|
||||
if (callsign == line.de) {
|
||||
row.id= line.rowid;
|
||||
} else if (callsign == line.dx) {
|
||||
row.id= line.rowid;
|
||||
}
|
||||
} else if (isnew) {
|
||||
row.className="table-info";
|
||||
}
|
||||
|
||||
//Column: DE search on QRZ
|
||||
const i_qrzde = document.createElement("i");
|
||||
i_qrzde.className="bi-search";
|
||||
i_qrzde.role="button";
|
||||
i_qrzde.ariaLabel=line.de;
|
||||
const a_qrzde = document.createElement("a");
|
||||
a_qrzde.href=qrz_url + line.de;
|
||||
a_qrzde.target="_blank";
|
||||
a_qrzde.rel="noopener";
|
||||
const span_qrzde = document.createElement("span");
|
||||
|
||||
//Mark DE if it found in callsign search
|
||||
if (line.de == callsign) {
|
||||
const mark_qrzde = document.createElement("mark");
|
||||
mark_qrzde.textContent = line.de;
|
||||
span_qrzde.appendChild(mark_qrzde)
|
||||
} else {
|
||||
span_qrzde.textContent='\xa0' + line.de;
|
||||
}
|
||||
|
||||
const td_qrzde = document.createElement("td");
|
||||
|
||||
a_qrzde.appendChild(i_qrzde);
|
||||
td_qrzde.appendChild(a_qrzde);
|
||||
td_qrzde.appendChild(span_qrzde);
|
||||
row.append(td_qrzde);
|
||||
|
||||
//Column: frequency
|
||||
var freq = Intl.NumberFormat('it-IT', {
|
||||
style: 'decimal'
|
||||
}).format(line.freq);
|
||||
|
||||
const span_freq = document.createElement("span");
|
||||
span_freq.className="badge bg-warning text-dark badge-responsive";
|
||||
span_freq.textContent=freq;
|
||||
|
||||
const td_freq = document.createElement("td");
|
||||
td_freq.appendChild(span_freq);
|
||||
|
||||
row.appendChild(td_freq);
|
||||
|
||||
//Column: DX (with ADXO Management)
|
||||
var adxo = findAdxo(my_adxo_events, line.dx);
|
||||
var adxo_link = '<a href=' + adxo_url + ' target=_blank rel=noopener >NG3K Website</a>'
|
||||
const i_qrzdx = document.createElement("i");
|
||||
i_qrzdx.className="bi-search";
|
||||
i_qrzdx.role="button";
|
||||
i_qrzdx.ariaLabel=line.dx;
|
||||
const a_qrzdx = document.createElement("a");
|
||||
a_qrzdx.href=qrz_url + line.dx;
|
||||
a_qrzdx.target="_blank";
|
||||
a_qrzdx.rel="noopener";
|
||||
const span_qrzdx = document.createElement("span");
|
||||
|
||||
//Mark DX if it found in callsign search
|
||||
const mark_qrzdx = document.createElement("mark");
|
||||
mark_qrzdx.textContent = line.dx;
|
||||
if (line.dx == callsign) {
|
||||
span_qrzdx.appendChild(mark_qrzdx)
|
||||
} else {
|
||||
span_qrzdx.textContent='\xa0' + line.dx;
|
||||
}
|
||||
|
||||
if (adxo != undefined) {
|
||||
const i_adxo = document.createElement("i");
|
||||
i_adxo.tabIndex=0;
|
||||
i_adxo.className="bi-megaphone-fill";
|
||||
i_adxo.style="color: cornflowerblue;";
|
||||
i_adxo.role="button";
|
||||
i_adxo.ariaLabel="dx_operations";
|
||||
i_adxo.setAttribute('data-bs-container', "body" );
|
||||
i_adxo.setAttribute('data-bs-toggle', "popover" );
|
||||
i_adxo.setAttribute('data-bs-trigger', "focus" );
|
||||
i_adxo.setAttribute('data-bs-sanitizer', "true");
|
||||
i_adxo.setAttribute('data-bs-placement', "auto");
|
||||
i_adxo.setAttribute('data-bs-html', "true");
|
||||
i_adxo.setAttribute('data-bs-title', "Announced DX Op.: " + adxo.summary);
|
||||
i_adxo.setAttribute('data-bs-content', adxo.description + "data from " + adxo_link);
|
||||
span_qrzdx.appendChild(i_adxo);
|
||||
}
|
||||
|
||||
const td_qrzdx = document.createElement("td");
|
||||
a_qrzdx.appendChild(i_qrzdx);
|
||||
td_qrzdx.appendChild(a_qrzdx);
|
||||
td_qrzdx.append(span_qrzdx);
|
||||
row.appendChild(td_qrzdx);
|
||||
|
||||
//Column: Flag
|
||||
try {
|
||||
const span_flag=document.createElement("span");
|
||||
span_flag.className="img-flag fi fi-" + line.iso;
|
||||
span_flag.setAttribute('data-bs-container', "body" );
|
||||
span_flag.setAttribute('data-bs-toggle', "popover" );
|
||||
span_flag.setAttribute('data-bs-trigger', "hover" );
|
||||
span_flag.setAttribute('data-bs-placement', "left");
|
||||
span_flag.setAttribute('data-bs-content', line.country );
|
||||
|
||||
const td_flag = document.createElement("td");
|
||||
td_flag.appendChild(span_flag);
|
||||
row.appendChild(td_flag);
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log("error creating flag");
|
||||
const td_flag = document.createElement("td");
|
||||
row.appendChild(td_flag);
|
||||
}
|
||||
|
||||
//Column: Country
|
||||
const td_country_code = document.createElement("td");
|
||||
td_country_code.className="d-none d-lg-table-cell d-xl-table-cell";
|
||||
td_country_code.textContent = line.country;
|
||||
row.appendChild(td_country_code);
|
||||
|
||||
//Column: Comment
|
||||
const td_comm = document.createElement("td");
|
||||
td_comm.className="d-none d-lg-table-cell d-xl-table-cell";
|
||||
td_comm.textContent = line.comm;
|
||||
row.appendChild(td_comm);
|
||||
|
||||
//Column: UTC
|
||||
let dt = new Date(line.time * 1000);
|
||||
let hh = '00' + dt.getUTCHours();
|
||||
hh = hh.substring(hh.length - 2, hh.length);
|
||||
let mi = '00' + dt.getMinutes();
|
||||
mi = mi.substring(mi.length - 2, mi.length);
|
||||
let dd = '00' + dt.getUTCDate();
|
||||
dd = dd.substring(dd.length - 2, dd.length);
|
||||
let mo = '00' + (Number(dt.getUTCMonth()) + 1);
|
||||
mo = mo.substring(mo.length - 2, mo.length);
|
||||
let yy = dt.getUTCFullYear();
|
||||
let tm = hh + ':' + mi;
|
||||
dt = dd + '/' + mo + '/' + yy;
|
||||
|
||||
const div_date_time = document.createElement("div");
|
||||
div_date_time.className="d-flex flex-column";
|
||||
const p_time = document.createElement("div");
|
||||
p_time.textContent=tm;
|
||||
div_date_time.appendChild(p_time);
|
||||
if (dt != dt_current) {
|
||||
const p_date = document.createElement("div");
|
||||
p_date.textContent=dt;
|
||||
div_date_time.appendChild(p_date);
|
||||
}
|
||||
|
||||
row.appendChild(div_date_time);
|
||||
|
||||
//Finally append the row created to the table
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the table with the spot
|
||||
*
|
||||
* @param data {json} The payload with all the spots received from cluster
|
||||
* @param rl {json} Row List
|
||||
* @param callsign {string} An optional parameter with the callsign to search
|
||||
*/
|
||||
build(data, callsign) {
|
||||
|
||||
if (data != null) {
|
||||
|
||||
//get current date
|
||||
let d = new Date();
|
||||
let dd_current = '00' + d.getUTCDate();
|
||||
dd_current = dd_current.substring(dd_current.length - 2, dd_current.length);
|
||||
let mo_current = '00' + (Number(d.getUTCMonth()) + 1);
|
||||
mo_current = mo_current.substring(mo_current.length - 2, mo_current.length);
|
||||
let yy_current = d.getUTCFullYear();
|
||||
let dt_current = dd_current + '/' + mo_current + '/' + yy_current;
|
||||
|
||||
//empty the table
|
||||
document.getElementById(this.selector).replaceChildren();
|
||||
|
||||
//insert in table new elements
|
||||
let merge_data = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
document.getElementById(this.selector).append(this.#buildRow(data[i],true,dt_current,callsign));
|
||||
merge_data.push(data[i]);
|
||||
}
|
||||
|
||||
//insert in html table previous elements
|
||||
for (let i = 0; i < this.current_data.length-data.length; i++) {
|
||||
document.getElementById(this.selector).append(this.#buildRow(this.current_data[i],false,dt_current,callsign));
|
||||
merge_data.push(this.current_data[i]);
|
||||
}
|
||||
|
||||
//replace current data with merge
|
||||
this.current_data=merge_data;
|
||||
|
||||
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
|
||||
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
|
||||
return new bootstrap.Popover(popoverTriggerEl)
|
||||
})
|
||||
|
||||
try {
|
||||
return;
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} //end class
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
* javascript used to popolate main table with spots
|
||||
* ******************************************************************************/
|
||||
const adxo_url = 'https://www.ng3k.com/misc/adxo.html'
|
||||
const qrz_url = 'https://www.qrz.com/db/'
|
||||
const tb = new table_builder('bodyspot');
|
||||
|
||||
/**
|
||||
* Decode Announced Dx Operation (ng3k)
|
||||
*
|
||||
* @param adxo {adxo} This is the json containing all the dxo events
|
||||
* @param callsign_to_find {callsign_to_find} The callsign of the current dx line
|
||||
*/
|
||||
function findAdxo(adxo, callsign_to_find) {
|
||||
if (adxo) {
|
||||
for (let i = 0; i < adxo.length; i++) {
|
||||
if (adxo[i].callsign == callsign_to_find) {
|
||||
return adxo[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Function to filter spot when pressed the search button on filter
|
||||
* This function trigger the search, also triggered by timer
|
||||
*/
|
||||
function mySearch(event) {
|
||||
|
||||
event.preventDefault();
|
||||
refresh_timer(); //force the call of query
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for construct query string for single value selection
|
||||
*
|
||||
* @param id {string} The html identifier used for filter
|
||||
* @param param {string}the parameter for the query
|
||||
* @param len {number} The maximum number of element that could be selected; use -1 if the filter permits a single selection
|
||||
* @param qrystr {string} Th initial query string to be completed with the new filter
|
||||
*/
|
||||
function getFilter(id, param, len, qrystr) {
|
||||
|
||||
selectedFilter = [].map.call(document.getElementById(id).selectedOptions, option => option.value);
|
||||
var qryFilter = '';
|
||||
if (selectedFilter.length < len || len == -1) {
|
||||
qryFilter = selectedFilter.map(function(el) {
|
||||
if (el) {
|
||||
return param + '=' + el;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}).join('&');
|
||||
qrystr = qrystr.concat('&'.concat(qryFilter));
|
||||
if (qrystr.substring(0, 1) == '&') {
|
||||
qrystr = qrystr.substring(1)
|
||||
}
|
||||
}
|
||||
|
||||
return qrystr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search / Filter cluster spot based on filter settings
|
||||
* Gets the filter values, constructs the query parameter and
|
||||
* make the request to the server
|
||||
*/
|
||||
function refresh_timer() {
|
||||
|
||||
let qryAll = '';
|
||||
|
||||
//get other filters
|
||||
qryAll = getFilter('band', 'b', 14, qryAll);
|
||||
qryAll = getFilter('de_re', 'e', 7, qryAll);
|
||||
qryAll = getFilter('dx_re', 'x', 7, qryAll);
|
||||
qryAll = getFilter('mode', 'm', 3, qryAll);
|
||||
qryAll = getFilter('cqdeInput', 'qe', -1, qryAll);
|
||||
qryAll = getFilter('cqdxInput', 'qx', -1, qryAll);
|
||||
|
||||
//Composing query string
|
||||
let qryString = ('spotlist?lr=').concat(tb.getLastRowId());
|
||||
if (qryAll) {
|
||||
qryString = qryString.concat(qryAll);
|
||||
}
|
||||
|
||||
console.log(qryString)
|
||||
//Open a new connection, using the GET request on the URL endpoint
|
||||
fetch(qryString)
|
||||
.then((response) => response.json())
|
||||
.then((data_new) => {
|
||||
try {
|
||||
tb.build(data_new);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(err.stack);
|
||||
console.log(data_new);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
13
static/js/table.min.js
vendored
13
static/js/table.min.js
vendored
@ -1,13 +0,0 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(a,b,c){a instanceof String&&(a=String(a));for(var d=a.length,k=0;k<d;k++){var g=a[k];if(b.call(c,g,k,a))return{i:k,v:g}}return{i:-1,v:void 0}};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
|
||||
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var k=a[d];k in c||(c[k]={});c=c[k]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,c){return $jscomp.findInternal(this,a,c).v}},"es6","es3");
|
||||
$jscomp.polyfill("Array.from",function(a){return a?a:function(a,c,d){c=null!=c?c:function(a){return a};var b=[],g="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];if("function"==typeof g){a=g.call(a);for(var f=0;!(g=a.next()).done;)b.push(c.call(d,g.value,f++))}else for(g=a.length,f=0;f<g;f++)b.push(c.call(d,a[f],f));return b}},"es6","es3");var adxo_url="https://www.ng3k.com/misc/adxo.html",qrz_url="https://www.qrz.com/db/";
|
||||
function findAdxo(a,b){if(a)for(var c=0;c<a.length;c++)if(a[c].callsign==b)return a[c]}
|
||||
function buildHtmlTable(a,b,c,d){if(null!=b){var k=[],g=new Date,f="00"+g.getUTCDate();f=f.substring(f.length-2,f.length);var h="00"+(Number(g.getUTCMonth())+1);h=h.substring(h.length-2,h.length);g=g.getUTCFullYear();dt_current=f+"/"+h+"/"+g;$(a).empty();for(var e=0;e<b.length;e++){k[e]=b[e].rowid;f=$('<tr id="'+b[e].rowid+'"/>');h=c.find(function(a){return a==b[e].rowid});void 0!=d?d==b[e].de?f=$('<tr id="'+b[e].rowid+'"/>'):d==b[e].dx&&(f=$('<tr id="'+b[e].rowid+'"/>')):void 0==h&&0<c.length&&(f=
|
||||
$('<tr class="table-info" id="'+b[e].rowid+'"/>'));de=b[e].de==d?"<mark>"+b[e].de+"</mark>":b[e].de;f.append($("<td/>").html('<a href="'+qrz_url+b[e].de+'" target="_blank" rel="noopener"><i class="bi-search" role="button" aria-label="'+b[e].de+'"></i></a><span> '+de+"</span></b>"));h=Intl.NumberFormat("it-IT",{style:"decimal"}).format(b[e].freq);f.append($("<td/>").html('<span class="badge bg-warning text-dark badge-responsive">'+h+"</span>"));dx=b[e].dx==d?"<mark>"+b[e].dx+"</mark>":b[e].dx;
|
||||
h=findAdxo(my_adxo_events,b[e].dx);g="<a href="+adxo_url+" target=_blank rel=noopener >NG3K Website</a>";void 0!=h&&(dx=dx+' <i tabindex="0" class="bi-megaphone-fill" style="color: cornflowerblue; " role="button" aria-label="dx_operations" data-bs-container="body" data-bs-toggle="popover" data-bs-trigger="focus" data-bs-sanitize="true" data-bs-placement="auto" data-bs-html="true" data-bs-title="Announced DX Op.: '+h.summary+'" data-bs-content="'+h.description+" data from  "+g+'"></i>');f.append($("<td/>").html('<a href="'+
|
||||
qrz_url+b[e].dx+'" target="_blank" rel="noopener"><i class="bi-search" role="button" aria-label="'+b[e].dx+'"></i></a><span> '+dx+"</span>"));try{f.append($("<td/>").html('<span class="img-flag fi fi-'+b[e].iso+'" data-bs-container="body" data-bs-toggle="popover" data-bs-trigger="hover" data-bs-placement="left" data-bs-content="'+b[e].country+'"></span>'))}catch(p){f.append($("<td/>"))}f.append($('<td class="d-none d-lg-table-cell d-xl-table-cell"/>').html(b[e].country));f.append($('<td class="d-none d-lg-table-cell d-xl-table-cell"/>').html(b[e].comm));
|
||||
var l=new Date(1E3*b[e].time);h="00"+l.getUTCHours();h=h.substring(h.length-2,h.length);g="00"+l.getMinutes();g=g.substring(g.length-2,g.length);var m="00"+l.getUTCDate();m=m.substring(m.length-2,m.length);var n="00"+(Number(l.getUTCMonth())+1);n=n.substring(n.length-2,n.length);l=l.getUTCFullYear();tm=h+":"+g;l=m+"/"+n+"/"+l;l==dt_current?f.append($("<td/>").html(tm)):f.append($("<td/>").html('<table class="table-sm table-borderless"><tbody><tr style="background-color:transparent"><td>'+tm+"</td></tr><tr><td>"+
|
||||
l+"</td></tr></tbody></table>"));$(a).append(f)}$(function(){$('[data-bs-toggle="popover"]').popover({container:a})});try{return Array.from(k)}catch(p){}}}function mySearch(a){a.preventDefault();myTimer()}
|
||||
function getFilter(a,b,c,d){selectedFilter=[].map.call(document.getElementById(a).selectedOptions,function(a){return a.value});a="";if(selectedFilter.length<c||-1==c)a=selectedFilter.map(function(a){return a?b+"="+a:""}).join("&"),d=d.concat("&".concat(a)),"&"==d.substring(0,1)&&(d=d.substring(1));return d}
|
||||
function myTimer(){var a=getFilter("band","b",14,"");a=getFilter("de_re","e",7,a);a=getFilter("dx_re","x",7,a);a=getFilter("mode","m",3,a);a=getFilter("cqdeInput","qe",-1,a);a=getFilter("cqdxInput","qx",-1,a);var b="spotlist";a&&(b=b.concat("?".concat(a)));fetch(b).then(function(a){return a.json()}).then(function(a){try{rows_list=buildHtmlTable("#bodyspot",a,rows_list)}catch(d){console.log(d),console.log(d.stack),console.log(a)}})};
|
@ -13,7 +13,7 @@
|
||||
<link rel="icon" href="/static/images/icons/spider_ico_master.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/images/icons/icon-apple.png">
|
||||
<link rel="manifest" href="/static/manifest.webmanifest">
|
||||
<link rel="stylesheet" href="/static/css/style.min.css">
|
||||
<link rel="stylesheet" href="/static/css/rel/style.min.css">
|
||||
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" as="style" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous" onload="this.rel='stylesheet' ">
|
||||
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"></noscript>
|
||||
@ -21,7 +21,7 @@
|
||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css" as="style" integrity="sha512-uvXdJud8WaOlQFjlz9B15Yy2Au/bMAvz79F7Xa6OakCl2jvQPdHD0hb3dEqZRdSwG4/sknePXlE7GiarwA/9Wg==" crossorigin="anonymous" onload="this.rel='stylesheet'" >
|
||||
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css"></noscript>
|
||||
|
||||
<script class="spiderscript" src="static/js/load_css.min.js"></script>
|
||||
<script src="static/js/rel/load_css.min.js"></script>
|
||||
{% endblock head %}
|
||||
</head>
|
||||
<body>
|
||||
@ -89,21 +89,18 @@
|
||||
<span id="version">v2.4.1</span>
|
||||
</div>
|
||||
</footer>
|
||||
<script async class="spiderscript" src="static/js/clock.min.js"></script>
|
||||
<script async class="spiderscript" src="static/js/copy_date.min.js"></script>
|
||||
<script async class="spiderscript" src="static/js/load-sw.min.js"></script>
|
||||
<script async src="static/js/rel/clock.min.js"></script>
|
||||
<script async src="static/js/rel/copy_date.min.js"></script>
|
||||
<script async src="static/js/rel/load-sw.min.js"></script>
|
||||
<script nonce="sedfGFG32xs">
|
||||
{% block app_data %}
|
||||
var my_callsign='{{callsign}}';
|
||||
{% endblock app_data %}
|
||||
</script>
|
||||
<script defer class="spiderscript" src="static/js/common.js"></script>
|
||||
<!--
|
||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
-->
|
||||
<script defer src="static/js/rel/common.min.js"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
|
||||
{% block app_scripts %}
|
||||
<script async class="spiderscript" src="static/js/callsign_search.js"></script>
|
||||
<script async src="static/js/rel/callsign_search.min.js"></script>
|
||||
{% endblock app_scripts %}
|
||||
{% block inline_scripts %}
|
||||
{% endblock inline_scripts %}
|
||||
@ -131,7 +128,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script defer class="spiderscript" src="static/js/cookie_consent.js"></script>
|
||||
<script defer src="static/js/rel/cookie_consent.min.js"></script>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,16 +1,17 @@
|
||||
{% extends "index.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>Spot search for a specific Callsign</title>
|
||||
{% endblock %}
|
||||
</head>
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">{{callsign}}</h1>
|
||||
<p class="lead text-light">Some statistics about this callsign</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
<div class="row mx-auto">
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">{{callsign}}</h1>
|
||||
<p class="lead text-light">Some statistics about this callsign</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
<div class="row mx-auto">
|
||||
{% endblock filters %}
|
||||
{% block inline_scripts %}
|
||||
<script defer class="spiderscript" src="static/js/callsign_inline.js"></script>
|
||||
<script defer src="static/js/rel/callsign_inline.min.js"></script>
|
||||
{% endblock %}
|
@ -1,5 +0,0 @@
|
||||
<input type="checkbox" id="category_{{ category.name }}"
|
||||
{% if category.default %}checked="checked"{% endif %}
|
||||
{% if category.is_required %}disabled="disabled"{% endif %}
|
||||
name="flask_consent_category" value="{{ category.name }}"/>
|
||||
<label for="category_{{ category.name }}">{{ category.title }}</label>
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>DX Cluster from IU1BOW: Cookies</title>
|
||||
@ -7,27 +8,40 @@
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
</head>
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">COOKIES</h1>
|
||||
<p class="lead text-light">WEB DX Cluster For HAM Radio</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
{% endblock %}
|
||||
{% block contents %}
|
||||
<div class="col mr-3 px-2">
|
||||
<p class="text-justify"><span class="font-weight-bold">Cookies</span> are small text files that can be used by websites to make a user's experience more efficient. This site uses different types of cookies. You can at any time change or withdraw
|
||||
your consent from the Cookies page on my website. Some cookies are placed by third party services that appear on our pages, for example if you view or listen to any embedded audio or video content. I don't control the setting of these cookies, so
|
||||
please check the websites of these third parties for more information about their cookies and how they manage them.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Necessary</span> cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">COOKIES</h1>
|
||||
<p class="lead text-light">WEB DX Cluster For HAM Radio</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
{% endblock %}
|
||||
{% block contents %}
|
||||
<div class="col mr-3 px-2">
|
||||
<p class="text-justify"><span class="font-weight-bold">Cookies</span> are small text files that can be used by
|
||||
websites to make a user's experience more efficient. This site uses different types of cookies. You can at any time
|
||||
change or withdraw
|
||||
your consent from the Cookies page on my website. Some cookies are placed by third party services that appear on our
|
||||
pages, for example if you view or listen to any embedded audio or video content. I don't control the setting of
|
||||
these cookies, so
|
||||
please check the websites of these third parties for more information about their cookies and how they manage them.
|
||||
</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Necessary</span> cookies help make a website usable by enabling
|
||||
basic functions like page navigation and access to secure areas of the website. The website cannot function properly
|
||||
without
|
||||
these cookies.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Preference</span> cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Statistic</span> cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Marketing</span> cookies are used to track visitors across websites. The intention is to display content, as well as ads that are relevant and engaging for the individual user and thereby more
|
||||
<p class="text-justify"><span class="font-weight-bold">Preference</span> cookies enable a website to remember
|
||||
information that changes the way the website behaves or looks, like your preferred language or the region that you
|
||||
are in.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Statistic</span> cookies help website owners to understand how
|
||||
visitors interact with websites by collecting and reporting information anonymously.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Marketing</span> cookies are used to track visitors across
|
||||
websites. The intention is to display content, as well as ads that are relevant and engaging for the individual user
|
||||
and thereby more
|
||||
valuable for publishers and third party advertisers.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Unclassified</span> cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block app_data %}
|
||||
{% endblock %}
|
||||
{% block app_scritps %}
|
||||
{% endblock %}
|
||||
<p class="text-justify"><span class="font-weight-bold">Unclassified</span> cookies are cookies that we are in the
|
||||
process of classifying, together with the providers of individual cookies.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block app_data %}
|
||||
{% endblock %}
|
||||
{% block app_scritps %}
|
||||
{% endblock %}
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>DX Cluster / DX Spot for Hamradio</title>
|
||||
@ -7,23 +8,24 @@
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
</head>
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||
<p class="lead text-light">Spots list</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
<div class="row mx-auto justify-content-between align-middle">
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||
<p class="lead text-light">Spots list</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
<div class="row mx-auto justify-content-between align-middle">
|
||||
<div class="mx-auto">
|
||||
<button class="btn btn-primary btn-sm" type="button" data-toggle="collapse" aria-expanded="false" aria-label="filter" data-bs-toggle="collapse" data-bs-target="#collapseFilters">
|
||||
<button class="btn btn-primary btn-sm" type="button" data-toggle="collapse" aria-expanded="false"
|
||||
aria-label="filter" data-bs-toggle="collapse" data-bs-target="#collapseFilters">
|
||||
<span class="bi-funnel-fill" role="button" aria-label="funnel-fill"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mx-auto">
|
||||
</div>
|
||||
<div class="row mx-auto">
|
||||
<!--Sidebar content-->
|
||||
<div class="col-md-auto ml-2 collapse rounded-sm shadow mb-5 bg-body" id="collapseFilters">
|
||||
<form method="POST" id="form-filters" enctype="multipart/form-data" >
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<form method="POST" id="form-filters" enctype="multipart/form-data">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<strong>Band</strong>
|
||||
<select class="form-select overflow-hidden" id="band" size="14" multiple>
|
||||
{% for dict_item in bands['bands']|reverse %}
|
||||
@ -65,48 +67,48 @@
|
||||
<div class="row">
|
||||
<strong>CQ De zone</strong>
|
||||
<div class="col">
|
||||
<select class="form-select" aria-label="cqde" aria-decribedby="cqde" id="cqdeInput" >
|
||||
<option value="" selected >All</option>
|
||||
<option value="1" >1 - northwestern zone of NA</option>
|
||||
<option value="2" >2 - northeastern zone of NA</option>
|
||||
<option value="3" >3 - western zone of NA</option>
|
||||
<option value="4" >4 - central zone of NA</option>
|
||||
<option value="5" >5 - eastern zone of NA</option>
|
||||
<option value="6" >6 - southern zone of NA</option>
|
||||
<option value="7" >7 - central american zone</option>
|
||||
<option value="8" >8 - west indies zone</option>
|
||||
<option value="9" >9 - northern zone of SA</option>
|
||||
<option value="10" >10 - western zone of SA</option>
|
||||
<option value="11" >11 - central zone of SA</option>
|
||||
<option value="12" >12 - southwest zone of SA</option>
|
||||
<option value="13" >13 - southeast zone of SA</option>
|
||||
<option value="14" >14 - western zone of EU</option>
|
||||
<option value="15" >15 - central EU zone</option>
|
||||
<option value="16" >16 - eastern zone of EU</option>
|
||||
<option value="17" >17 - western zone of siberia</option>
|
||||
<option value="18" >18 - central siberian zone</option>
|
||||
<option value="19" >19 - eastern siberian zone</option>
|
||||
<option value="20" >20 - balkan zone</option>
|
||||
<option value="21" >21 - southwestern zone of AS</option>
|
||||
<option value="22" >22 - southern zone of AS</option>
|
||||
<option value="23" >23 - central zone of AS</option>
|
||||
<option value="24" >24 - eastern zone of AS</option>
|
||||
<option value="25" >25 - japanese zone</option>
|
||||
<option value="26" >26 - southeastern zone of AS</option>
|
||||
<option value="27" >27 - philippine zone</option>
|
||||
<option value="28" >28 - indonesian zone</option>
|
||||
<option value="29" >29 - western zone of australia</option>
|
||||
<option value="30" >30 - eastern zone of australia</option>
|
||||
<option value="31" >31 - central pacific zone</option>
|
||||
<option value="32" >32 - new zealand zone</option>
|
||||
<option value="33" >33 - northwestern zone of AF</option>
|
||||
<option value="34" >34 - northeastern zone of AF</option>
|
||||
<option value="35" >35 - central zone of AF</option>
|
||||
<option value="36" >36 - equatorial zone of AF</option>
|
||||
<option value="37" >37 - eastern zone of AF</option>
|
||||
<option value="38" >38 - south AF zone</option>
|
||||
<option value="39" >39 - madagascar zone</option>
|
||||
<option value="40" >40 - north atlantic zone</option>
|
||||
<select class="form-select" aria-label="cqde" aria-decribedby="cqde" id="cqdeInput">
|
||||
<option value="" selected>All</option>
|
||||
<option value="1">1 - northwestern zone of NA</option>
|
||||
<option value="2">2 - northeastern zone of NA</option>
|
||||
<option value="3">3 - western zone of NA</option>
|
||||
<option value="4">4 - central zone of NA</option>
|
||||
<option value="5">5 - eastern zone of NA</option>
|
||||
<option value="6">6 - southern zone of NA</option>
|
||||
<option value="7">7 - central american zone</option>
|
||||
<option value="8">8 - west indies zone</option>
|
||||
<option value="9">9 - northern zone of SA</option>
|
||||
<option value="10">10 - western zone of SA</option>
|
||||
<option value="11">11 - central zone of SA</option>
|
||||
<option value="12">12 - southwest zone of SA</option>
|
||||
<option value="13">13 - southeast zone of SA</option>
|
||||
<option value="14">14 - western zone of EU</option>
|
||||
<option value="15">15 - central EU zone</option>
|
||||
<option value="16">16 - eastern zone of EU</option>
|
||||
<option value="17">17 - western zone of siberia</option>
|
||||
<option value="18">18 - central siberian zone</option>
|
||||
<option value="19">19 - eastern siberian zone</option>
|
||||
<option value="20">20 - balkan zone</option>
|
||||
<option value="21">21 - southwestern zone of AS</option>
|
||||
<option value="22">22 - southern zone of AS</option>
|
||||
<option value="23">23 - central zone of AS</option>
|
||||
<option value="24">24 - eastern zone of AS</option>
|
||||
<option value="25">25 - japanese zone</option>
|
||||
<option value="26">26 - southeastern zone of AS</option>
|
||||
<option value="27">27 - philippine zone</option>
|
||||
<option value="28">28 - indonesian zone</option>
|
||||
<option value="29">29 - western zone of australia</option>
|
||||
<option value="30">30 - eastern zone of australia</option>
|
||||
<option value="31">31 - central pacific zone</option>
|
||||
<option value="32">32 - new zealand zone</option>
|
||||
<option value="33">33 - northwestern zone of AF</option>
|
||||
<option value="34">34 - northeastern zone of AF</option>
|
||||
<option value="35">35 - central zone of AF</option>
|
||||
<option value="36">36 - equatorial zone of AF</option>
|
||||
<option value="37">37 - eastern zone of AF</option>
|
||||
<option value="38">38 - south AF zone</option>
|
||||
<option value="39">39 - madagascar zone</option>
|
||||
<option value="40">40 - north atlantic zone</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -114,55 +116,57 @@
|
||||
<div class="row">
|
||||
<strong>CQ Dx zone</strong>
|
||||
<div class="col">
|
||||
<select class="form-select" aria-label="cqdx" aria-decribedby="cqdx" id="cqdxInput" >
|
||||
<select class="form-select" aria-label="cqdx" aria-decribedby="cqdx" id="cqdxInput">
|
||||
<option value="" selected>All</option>
|
||||
<option value="1" >1 - northwestern zone of NA</option>
|
||||
<option value="2" >2 - northeastern zone of NA</option>
|
||||
<option value="3" >3 - western zone of NA</option>
|
||||
<option value="4" >4 - central zone of NA</option>
|
||||
<option value="5" >5 - eastern zone of NA</option>
|
||||
<option value="6" >6 - southern zone of NA</option>
|
||||
<option value="7" >7 - central american zone</option>
|
||||
<option value="8" >8 - west indies zone</option>
|
||||
<option value="9" >9 - northern zone of SA</option>
|
||||
<option value="10" >10 - western zone of SA</option>
|
||||
<option value="11" >11 - central zone of SA</option>
|
||||
<option value="12" >12 - southwest zone of SA</option>
|
||||
<option value="13" >13 - southeast zone of SA</option>
|
||||
<option value="14" >14 - western zone of EU</option>
|
||||
<option value="15" >15 - central EU zone</option>
|
||||
<option value="16" >16 - eastern zone of EU</option>
|
||||
<option value="17" >17 - western zone of siberia</option>
|
||||
<option value="18" >18 - central siberian zone</option>
|
||||
<option value="19" >19 - eastern siberian zone</option>
|
||||
<option value="20" >20 - balkan zone</option>
|
||||
<option value="21" >21 - southwestern zone of AS</option>
|
||||
<option value="22" >22 - southern zone of AS</option>
|
||||
<option value="23" >23 - central zone of AS</option>
|
||||
<option value="24" >24 - eastern zone of AS</option>
|
||||
<option value="25" >25 - japanese zone</option>
|
||||
<option value="26" >26 - southeastern zone of AS</option>
|
||||
<option value="27" >27 - philippine zone</option>
|
||||
<option value="28" >28 - indonesian zone</option>
|
||||
<option value="29" >29 - western zone of australia</option>
|
||||
<option value="30" >30 - eastern zone of australia</option>
|
||||
<option value="31" >31 - central pacific zone</option>
|
||||
<option value="32" >32 - new zealand zone</option>
|
||||
<option value="33" >33 - northwestern zone of AF</option>
|
||||
<option value="34" >34 - northeastern zone of AF</option>
|
||||
<option value="35" >35 - central zone of AF</option>
|
||||
<option value="36" >36 - equatorial zone of AF</option>
|
||||
<option value="37" >37 - eastern zone of AF</option>
|
||||
<option value="38" >38 - south AF zone</option>
|
||||
<option value="39" >39 - madagascar zone</option>
|
||||
<option value="40" >40 - north atlantic zone</option>
|
||||
<option value="1">1 - northwestern zone of NA</option>
|
||||
<option value="2">2 - northeastern zone of NA</option>
|
||||
<option value="3">3 - western zone of NA</option>
|
||||
<option value="4">4 - central zone of NA</option>
|
||||
<option value="5">5 - eastern zone of NA</option>
|
||||
<option value="6">6 - southern zone of NA</option>
|
||||
<option value="7">7 - central american zone</option>
|
||||
<option value="8">8 - west indies zone</option>
|
||||
<option value="9">9 - northern zone of SA</option>
|
||||
<option value="10">10 - western zone of SA</option>
|
||||
<option value="11">11 - central zone of SA</option>
|
||||
<option value="12">12 - southwest zone of SA</option>
|
||||
<option value="13">13 - southeast zone of SA</option>
|
||||
<option value="14">14 - western zone of EU</option>
|
||||
<option value="15">15 - central EU zone</option>
|
||||
<option value="16">16 - eastern zone of EU</option>
|
||||
<option value="17">17 - western zone of siberia</option>
|
||||
<option value="18">18 - central siberian zone</option>
|
||||
<option value="19">19 - eastern siberian zone</option>
|
||||
<option value="20">20 - balkan zone</option>
|
||||
<option value="21">21 - southwestern zone of AS</option>
|
||||
<option value="22">22 - southern zone of AS</option>
|
||||
<option value="23">23 - central zone of AS</option>
|
||||
<option value="24">24 - eastern zone of AS</option>
|
||||
<option value="25">25 - japanese zone</option>
|
||||
<option value="26">26 - southeastern zone of AS</option>
|
||||
<option value="27">27 - philippine zone</option>
|
||||
<option value="28">28 - indonesian zone</option>
|
||||
<option value="29">29 - western zone of australia</option>
|
||||
<option value="30">30 - eastern zone of australia</option>
|
||||
<option value="31">31 - central pacific zone</option>
|
||||
<option value="32">32 - new zealand zone</option>
|
||||
<option value="33">33 - northwestern zone of AF</option>
|
||||
<option value="34">34 - northeastern zone of AF</option>
|
||||
<option value="35">35 - central zone of AF</option>
|
||||
<option value="36">36 - equatorial zone of AF</option>
|
||||
<option value="37">37 - eastern zone of AF</option>
|
||||
<option value="38">38 - south AF zone</option>
|
||||
<option value="39">39 - madagascar zone</option>
|
||||
<option value="40">40 - north atlantic zone</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<p></p>
|
||||
<div class="mx-auto">
|
||||
<button type="submit" class="btn btn-primary btn-block w-100" aria-pressed="true" data-toggle="collapse" data-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters" aria-label="filter">Search</button>
|
||||
<button type="submit" class="btn btn-primary btn-block w-100" aria-pressed="true" data-toggle="collapse"
|
||||
data-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters"
|
||||
aria-label="filter">Search</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -187,24 +191,21 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock contents %}
|
||||
{% block app_data %}
|
||||
{{ super() }}
|
||||
|
||||
var timer_interval_json = {{timer_interval}};
|
||||
var my_adxo_events_json='{{ adxo_events|tojson|safe }}';
|
||||
var continents_cq={{continents["continents"]|tojson|safe}};
|
||||
var band_frequencies={{bands["bands"]|tojson|safe}};
|
||||
|
||||
{% endblock app_data %}
|
||||
{% block app_scripts %}
|
||||
{{ super() }}
|
||||
<script defer class="spiderscript" src="static/js/table.js"></script>
|
||||
{% endblock %}
|
||||
{% block inline_scripts %}
|
||||
<script defer class="spiderscript" src="static/js/index_inline.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
{% endblock contents %}
|
||||
{% block app_data %}
|
||||
{{ super() }}
|
||||
|
||||
var timer_interval_json = {{timer_interval}};
|
||||
var my_adxo_events_json='{{ adxo_events|tojson|safe }}';
|
||||
var continents_cq={{continents["continents"]|tojson|safe}};
|
||||
var band_frequencies={{bands["bands"]|tojson|safe}};
|
||||
|
||||
{% endblock app_data %}
|
||||
{% block app_scripts %}
|
||||
{{ super() }}
|
||||
<script defer src="static/js/rel/table.min.js"></script>
|
||||
{% endblock %}
|
||||
{% block inline_scripts %}
|
||||
<script defer src="static/js/rel/index_inline.min.js"></script>
|
||||
{% endblock %}
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>DX Cluster from IU1BOW: OFFLINE</title>
|
||||
@ -8,33 +9,34 @@
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
</head>
|
||||
{% block menu %}
|
||||
{{ super() }}
|
||||
{% block callsign %}
|
||||
{% endblock callsign %}
|
||||
{% endblock menu %}
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||
<p class="lead text-light">Spots list</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
{% endblock %}
|
||||
{% block contents %}
|
||||
{% block menu %}
|
||||
{{ super() }}
|
||||
{% block callsign %}
|
||||
{% endblock callsign %}
|
||||
{% endblock menu %}
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||
<p class="lead text-light">Spots list</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
{% endblock %}
|
||||
{% block contents %}
|
||||
<!-- <div class="container">
|
||||
<div class="row">
|
||||
<div class="col align-self-center"> -->
|
||||
<div class="jumbotron alert alert-warning" role="alert">
|
||||
<div class="jumbotron alert alert-warning" role="alert">
|
||||
<h2 class="display-4">No internet connection</h2>
|
||||
<p class="lead">The features in this area require Internet connectivity. Please connect your computer to the Internet</p>
|
||||
<p class="lead">The features in this area require Internet connectivity. Please connect your computer to the
|
||||
Internet</p>
|
||||
<p class="lead">
|
||||
<a class="btn btn-primary btn-lg" href="/" role="button">Try again</a>
|
||||
</p>
|
||||
<!-- </div>
|
||||
<!-- </div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block app_data %}
|
||||
{% endblock %}
|
||||
{% block app_scritps %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
{% block app_data %}
|
||||
{% endblock %}
|
||||
{% block app_scritps %}
|
||||
{% endblock %}
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>Some charts end stats from the dx clustes node</title>
|
||||
@ -6,28 +7,30 @@
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<meta http-equiv="refresh" content="300">
|
||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.1/echarts.min.js" integrity="sha512-OTbGFYPLe3jhy4bUwbB8nls0TFgz10kn0TLkmyA+l3FyivDs31zsXCjOis7YGDtE2Jsy0+fzW+3/OVoPVujPmQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.1/echarts.min.js"
|
||||
integrity="sha512-OTbGFYPLe3jhy4bUwbB8nls0TFgz10kn0TLkmyA+l3FyivDs31zsXCjOis7YGDtE2Jsy0+fzW+3/OVoPVujPmQ=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
{% endblock %}
|
||||
|
||||
</head>
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">PLOTS & STATS</h1>
|
||||
<p class="lead text-light">Some statistics about this node</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
{% endblock %}
|
||||
{% block contents %}
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">PLOTS & STATS</h1>
|
||||
<p class="lead text-light">Some statistics about this node</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
{% endblock %}
|
||||
{% block contents %}
|
||||
|
||||
<div class="d-flex flex-wrap" id="dashboard">
|
||||
<div class="d-flex flex-wrap" id="dashboard">
|
||||
<div class="shadow-lg bg-body mb-5 rounded" id="form-band_activity">
|
||||
<div class="d-flex flex-column">
|
||||
<form method="POST" id="form-continents" enctype="multipart/form-data" >
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<form method="POST" id="form-continents" enctype="multipart/form-data">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<div class="container">
|
||||
Your continent is:
|
||||
<select class="form-select flex-shrink" aria-label="continent" id="continentInput">
|
||||
{% for dict_item in continents['continents']%}
|
||||
<option value="{{dict_item['id']}}" >{{dict_item["description"]}}</option>
|
||||
<option value="{{dict_item['id']}}">{{dict_item["description"]}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@ -42,16 +45,14 @@
|
||||
<div class="shadow-lg mb-5 bg-body rounded" id="chart-hour_band"></div>
|
||||
|
||||
<a class="shadow-lg mb-5 bg-body rounded" href="https://sidc.be/silso/" target="_blank" rel="noopener noreferrer">
|
||||
<img src="https://sidc.be/silso/IMAGES/GRAPHICS/prediSC.png"class="img-fluid" id="silo-propagation-img" alt="propagation trend">
|
||||
<img src="https://sidc.be/silso/IMAGES/GRAPHICS/prediSC.png" class="img-fluid" id="silo-propagation-img"
|
||||
alt="propagation trend">
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<div class="shadow-lg mb-5 bg-body rounded" id="chart-dx_spots_x_month"></div>
|
||||
|
||||
<div class="shadow-lg mb-5 bg-body rounded" id="chart-dx_spots_trend"></div>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="shadow-lg mb-5 bg-body rounded">
|
||||
<strong>Physically connected callsigns to {{ mycallsign }}</strong>
|
||||
@ -80,21 +81,21 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock contents %}
|
||||
</div>
|
||||
{% endblock contents %}
|
||||
|
||||
{% block app_data %}
|
||||
{{ super() }}
|
||||
var continents_cq={{continents["continents"]|tojson|safe}};
|
||||
var band_frequencies={{bands["bands"]|tojson|safe}};
|
||||
{% endblock app_data %}
|
||||
{% block app_data %}
|
||||
{{ super() }}
|
||||
var continents_cq={{continents["continents"]|tojson|safe}};
|
||||
var band_frequencies={{bands["bands"]|tojson|safe}};
|
||||
{% endblock app_data %}
|
||||
|
||||
{% block app_scripts %}
|
||||
{{ super() }}
|
||||
<script defer class="spiderscript" src="static/js/plot_band_activity.js"></script>
|
||||
<script defer class="spiderscript" src="static/js/plot_world_dx_spots_live.js"></script>
|
||||
<script defer class="spiderscript" src="static/js/plot_hour_band.js"></script>
|
||||
<script defer class="spiderscript" src="static/js/plot_dx_spots_trend.js"></script>
|
||||
<script defer class="spiderscript" src="static/js/plot_dx_spots_per_month.js"></script>
|
||||
{% block app_scripts %}
|
||||
{{ super() }}
|
||||
<script defer src="static/js/rel/plot_band_activity.min.js"></script>
|
||||
<script defer src="static/js/rel/plot_world_dx_spots_live.min.js"></script>
|
||||
<script defer src="static/js/rel/plot_hour_band.min.js"></script>
|
||||
<script defer src="static/js/rel/plot_dx_spots_trend.min.js"></script>
|
||||
<script defer src="static/js/rel/plot_dx_spots_per_month.min.js"></script>
|
||||
|
||||
{% endblock app_scripts %}
|
||||
{% endblock app_scripts %}
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>DX Cluster from IU1BOW: Privacy</title>
|
||||
@ -7,69 +8,113 @@
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
</head>
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">PRIVACY</h1>
|
||||
<p class="lead text-light">WEB DX Cluster For HAM Radio</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
{% endblock %}
|
||||
{% block contents %}
|
||||
<div class="col mr-3 px-2">
|
||||
<h1>Privacy Policy for this web site</h1>
|
||||
<p>At this web site, one of our main priorities is the privacy of our visitors. This Privacy Policy document contains types of information that is collected and recorded by this web site and how we use it.</p>
|
||||
<p>If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact us.</p>
|
||||
<p>This Privacy Policy applies only to our online activities and is valid for visitors to our website with regards to the information that they shared and/or collect in this web site. This policy is not applicable to any information collected offline or via channels other than this website. Our Privacy Policy was created with the help of the <a href="https://www.privacypolicygenerator.info">Privacy Policy Generator</a> and the <a href="https://www.generateprivacypolicy.com/">Free Privacy Policy Generator</a>.</p>
|
||||
<h2>Consent</h2>
|
||||
<p>By using our website, you hereby consent to our Privacy Policy and agree to its terms. For our Terms and Conditions, please visit the <a href="https://www.privacypolicyonline.com/terms-conditions-generator/">Terms & Conditions Generator</a>.</p>
|
||||
<h2>Information we collect</h2>
|
||||
<p>The personal information that you are asked to provide, and the reasons why you are asked to provide it, will be made clear to you at the point we ask you to provide your personal information.</p>
|
||||
<p>If you contact us directly, we may receive additional information about you such as your name, email address, phone number, the contents of the message and/or attachments you may send us, and any other information you may choose to provide.</p>
|
||||
<p>When you register for an Account, we may ask for your contact information, including items such as name, company name, address, email address, and telephone number.</p>
|
||||
<h2>How we use your information</h2>
|
||||
<p>We use the information we collect in various ways, including to:</p>
|
||||
<ul>
|
||||
<li>Provide, operate, and maintain our webste</li>
|
||||
<li>Improve, personalize, and expand our webste</li>
|
||||
<li>Understand and analyze how you use our webste</li>
|
||||
<li>Develop new products, services, features, and functionality</li>
|
||||
<li>Communicate with you, either directly or through one of our partners, including for customer service, to provide you with updates and other information relating to the webste, and for marketing and promotional purposes</li>
|
||||
<li>Send you emails</li>
|
||||
<li>Find and prevent fraud</li>
|
||||
</ul>
|
||||
<h2>Log Files</h2>
|
||||
<p>this web site follows a standard procedure of using log files. These files log visitors when they visit websites. All hosting companies do this and a part of hosting services' analytics. The information collected by log files include internet protocol (IP) addresses, browser type, Internet Service Provider (ISP), date and time stamp, referring/exit pages, and possibly the number of clicks. These are not linked to any information that is personally identifiable. The purpose of the information is for analyzing trends, administering the site, tracking users' movement on the website, and gathering demographic information.</p>
|
||||
<h2>Cookies and Web Beacons</h2>
|
||||
<p>Like any other website, this web site uses 'cookies'. These cookies are used to store information including visitors' preferences, and the pages on the website that the visitor accessed or visited. The information is used to optimize the users' experience by customizing our web page content based on visitors' browser type and/or other information.</p>
|
||||
<p>For more general information on cookies, please read <a href="https://www.privacypolicies.com/blog/cookies/">"What Are Cookies"</a>.</p>
|
||||
<h2>Advertising Partners Privacy Policies</h2>
|
||||
<P>You may consult this list to find the Privacy Policy for each of the advertising partners of this web site.</p>
|
||||
<p>Third-party ad servers or ad networks uses technologies like cookies, JavaScript, or Web Beacons that are used in their respective advertisements and links that appear on this web site, which are sent directly to users' browser. They automatically receive your IP address when this occurs. These technologies are used to measure the effectiveness of their advertising campaigns and/or to personalize the advertising content that you see on websites that you visit.</p>
|
||||
<p>Note that this web site has no access to or control over these cookies that are used by third-party advertisers.</p>
|
||||
<h2>Third Party Privacy Policies</h2>
|
||||
<p>this web site's Privacy Policy does not apply to other advertisers or websites. Thus, we are advising you to consult the respective Privacy Policies of these third-party ad servers for more detailed information. It may include their practices and instructions about how to opt-out of certain options. </p>
|
||||
<p>You can choose to disable cookies through your individual browser options. To know more detailed information about cookie management with specific web browsers, it can be found at the browsers' respective websites.</p>
|
||||
<h2>CCPA Privacy Rights (Do Not Sell My Personal Information)</h2>
|
||||
<p>Under the CCPA, among other rights, California consumers have the right to:</p>
|
||||
<p>Request that a business that collects a consumer's personal data disclose the categories and specific pieces of personal data that a business has collected about consumers.</p>
|
||||
<p>Request that a business delete any personal data about the consumer that a business has collected.</p>
|
||||
<p>Request that a business that sells a consumer's personal data, not sell the consumer's personal data.</p>
|
||||
<p>If you make a request, we have one month to respond to you. If you would like to exercise any of these rights, please contact us.</p>
|
||||
<h2>GDPR Data Protection Rights</h2>
|
||||
<p>We would like to make sure you are fully aware of all of your data protection rights. Every user is entitled to the following:</p>
|
||||
<p>The right to access – You have the right to request copies of your personal data. We may charge you a small fee for this service.</p>
|
||||
<p>The right to rectification – You have the right to request that we correct any information you believe is inaccurate. You also have the right to request that we complete the information you believe is incomplete.</p>
|
||||
<p>The right to erasure – You have the right to request that we erase your personal data, under certain conditions.</p>
|
||||
<p>The right to restrict processing – You have the right to request that we restrict the processing of your personal data, under certain conditions.</p>
|
||||
<p>The right to object to processing – You have the right to object to our processing of your personal data, under certain conditions.</p>
|
||||
<p>The right to data portability – You have the right to request that we transfer the data that we have collected to another organization, or directly to you, under certain conditions.</p>
|
||||
<p>If you make a request, we have one month to respond to you. If you would like to exercise any of these rights, please contact us.</p>
|
||||
<h2>Children's Information</h2>
|
||||
<p>Another part of our priority is adding protection for children while using the internet. We encourage parents and guardians to observe, participate in, and/or monitor and guide their online activity.</p>
|
||||
<p>this web site does not knowingly collect any Personal Identifiable Information from children under the age of 13. If you think that your child provided this kind of information on our website, we strongly encourage you to contact us immediately and we will do our best efforts to promptly remove such information from our records.</p>
|
||||
{% block titles %}
|
||||
<h1 class="display-4 text-white">PRIVACY</h1>
|
||||
<p class="lead text-light">WEB DX Cluster For HAM Radio</p>
|
||||
{% endblock %}
|
||||
{% block filters %}
|
||||
{% endblock %}
|
||||
{% block contents %}
|
||||
<div class="col mr-3 px-2">
|
||||
<h1>Privacy Policy for this web site</h1>
|
||||
<p>At this web site, one of our main priorities is the privacy of our visitors. This Privacy Policy document
|
||||
contains types of information that is collected and recorded by this web site and how we use it.</p>
|
||||
<p>If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact
|
||||
us.</p>
|
||||
<p>This Privacy Policy applies only to our online activities and is valid for visitors to our website with regards
|
||||
to the information that they shared and/or collect in this web site. This policy is not applicable to any
|
||||
information collected offline or via channels other than this website. Our Privacy Policy was created with the
|
||||
help of the <a href="https://www.privacypolicygenerator.info">Privacy Policy Generator</a> and the <a
|
||||
href="https://www.generateprivacypolicy.com/">Free Privacy Policy Generator</a>.</p>
|
||||
<h2>Consent</h2>
|
||||
<p>By using our website, you hereby consent to our Privacy Policy and agree to its terms. For our Terms and
|
||||
Conditions, please visit the <a href="https://www.privacypolicyonline.com/terms-conditions-generator/">Terms &
|
||||
Conditions Generator</a>.</p>
|
||||
<h2>Information we collect</h2>
|
||||
<p>The personal information that you are asked to provide, and the reasons why you are asked to provide it, will be
|
||||
made clear to you at the point we ask you to provide your personal information.</p>
|
||||
<p>If you contact us directly, we may receive additional information about you such as your name, email address,
|
||||
phone number, the contents of the message and/or attachments you may send us, and any other information you may
|
||||
choose to provide.</p>
|
||||
<p>When you register for an Account, we may ask for your contact information, including items such as name, company
|
||||
name, address, email address, and telephone number.</p>
|
||||
<h2>How we use your information</h2>
|
||||
<p>We use the information we collect in various ways, including to:</p>
|
||||
<ul>
|
||||
<li>Provide, operate, and maintain our webste</li>
|
||||
<li>Improve, personalize, and expand our webste</li>
|
||||
<li>Understand and analyze how you use our webste</li>
|
||||
<li>Develop new products, services, features, and functionality</li>
|
||||
<li>Communicate with you, either directly or through one of our partners, including for customer service, to
|
||||
provide you with updates and other information relating to the webste, and for marketing and promotional
|
||||
purposes</li>
|
||||
<li>Send you emails</li>
|
||||
<li>Find and prevent fraud</li>
|
||||
</ul>
|
||||
<h2>Log Files</h2>
|
||||
<p>this web site follows a standard procedure of using log files. These files log visitors when they visit websites.
|
||||
All hosting companies do this and a part of hosting services' analytics. The information collected by log files
|
||||
include internet protocol (IP) addresses, browser type, Internet Service Provider (ISP), date and time stamp,
|
||||
referring/exit pages, and possibly the number of clicks. These are not linked to any information that is
|
||||
personally identifiable. The purpose of the information is for analyzing trends, administering the site,
|
||||
tracking users' movement on the website, and gathering demographic information.</p>
|
||||
<h2>Cookies and Web Beacons</h2>
|
||||
<p>Like any other website, this web site uses 'cookies'. These cookies are used to store information including
|
||||
visitors' preferences, and the pages on the website that the visitor accessed or visited. The information is
|
||||
used to optimize the users' experience by customizing our web page content based on visitors' browser type
|
||||
and/or other information.</p>
|
||||
<p>For more general information on cookies, please read <a
|
||||
href="https://www.privacypolicies.com/blog/cookies/">"What Are Cookies"</a>.</p>
|
||||
<h2>Advertising Partners Privacy Policies</h2>
|
||||
<P>You may consult this list to find the Privacy Policy for each of the advertising partners of this web site.</p>
|
||||
<p>Third-party ad servers or ad networks uses technologies like cookies, JavaScript, or Web Beacons that are used in
|
||||
their respective advertisements and links that appear on this web site, which are sent directly to users'
|
||||
browser. They automatically receive your IP address when this occurs. These technologies are used to measure the
|
||||
effectiveness of their advertising campaigns and/or to personalize the advertising content that you see on
|
||||
websites that you visit.</p>
|
||||
<p>Note that this web site has no access to or control over these cookies that are used by third-party advertisers.
|
||||
</p>
|
||||
<h2>Third Party Privacy Policies</h2>
|
||||
<p>this web site's Privacy Policy does not apply to other advertisers or websites. Thus, we are advising you to
|
||||
consult the respective Privacy Policies of these third-party ad servers for more detailed information. It may
|
||||
include their practices and instructions about how to opt-out of certain options. </p>
|
||||
<p>You can choose to disable cookies through your individual browser options. To know more detailed information
|
||||
about cookie management with specific web browsers, it can be found at the browsers' respective websites.</p>
|
||||
<h2>CCPA Privacy Rights (Do Not Sell My Personal Information)</h2>
|
||||
<p>Under the CCPA, among other rights, California consumers have the right to:</p>
|
||||
<p>Request that a business that collects a consumer's personal data disclose the categories and specific pieces of
|
||||
personal data that a business has collected about consumers.</p>
|
||||
<p>Request that a business delete any personal data about the consumer that a business has collected.</p>
|
||||
<p>Request that a business that sells a consumer's personal data, not sell the consumer's personal data.</p>
|
||||
<p>If you make a request, we have one month to respond to you. If you would like to exercise any of these rights,
|
||||
please contact us.</p>
|
||||
<h2>GDPR Data Protection Rights</h2>
|
||||
<p>We would like to make sure you are fully aware of all of your data protection rights. Every user is entitled to
|
||||
the following:</p>
|
||||
<p>The right to access – You have the right to request copies of your personal data. We may charge you a small fee
|
||||
for this service.</p>
|
||||
<p>The right to rectification – You have the right to request that we correct any information you believe is
|
||||
inaccurate. You also have the right to request that we complete the information you believe is incomplete.</p>
|
||||
<p>The right to erasure – You have the right to request that we erase your personal data, under certain conditions.
|
||||
</p>
|
||||
<p>The right to restrict processing – You have the right to request that we restrict the processing of your personal
|
||||
data, under certain conditions.</p>
|
||||
<p>The right to object to processing – You have the right to object to our processing of your personal data, under
|
||||
certain conditions.</p>
|
||||
<p>The right to data portability – You have the right to request that we transfer the data that we have collected to
|
||||
another organization, or directly to you, under certain conditions.</p>
|
||||
<p>If you make a request, we have one month to respond to you. If you would like to exercise any of these rights,
|
||||
please contact us.</p>
|
||||
<h2>Children's Information</h2>
|
||||
<p>Another part of our priority is adding protection for children while using the internet. We encourage parents and
|
||||
guardians to observe, participate in, and/or monitor and guide their online activity.</p>
|
||||
<p>this web site does not knowingly collect any Personal Identifiable Information from children under the age of 13.
|
||||
If you think that your child provided this kind of information on our website, we strongly encourage you to
|
||||
contact us immediately and we will do our best efforts to promptly remove such information from our records.</p>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block app_data %}
|
||||
{% endblock %}
|
||||
{% block app_scritps %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block app_data %}
|
||||
{% endblock %}
|
||||
{% block app_scritps %}
|
||||
{% endblock %}
|
21
test.sh
21
test.sh
@ -1,8 +1,21 @@
|
||||
if [ "$1" == "-b" ]; then
|
||||
if [ $# -gt 0 ]
|
||||
then
|
||||
cd scripts || exit
|
||||
./build.sh
|
||||
if ! ./build.sh ${1}
|
||||
then
|
||||
cd ..
|
||||
echo "terminated"
|
||||
exit 1
|
||||
fi
|
||||
cd ..
|
||||
fi
|
||||
#python3 webapp.py
|
||||
flask --app webapp.py run
|
||||
|
||||
if [ "$1" == "-d" ]; then
|
||||
flask --app webapp.py --debug run
|
||||
else
|
||||
flask --app webapp.py run
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
434
webapp.py
434
webapp.py
@ -1,4 +1,4 @@
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import os
|
||||
import flask
|
||||
from flask import request, render_template, jsonify
|
||||
@ -23,140 +23,162 @@ logger = logging.getLogger(__name__)
|
||||
logger.info("Start")
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.config["DEBUG"] = False
|
||||
app.config['SECRET_KEY'] = 'secret!'
|
||||
app.config["SECRET_KEY"] = "secret!"
|
||||
app.config.update(
|
||||
SESSION_COOKIE_SECURE=True, #If you use http change it to False
|
||||
SESSION_COOKIE_SECURE=True,
|
||||
SESSION_COOKIE_HTTPONLY=True,
|
||||
SESSION_COOKIE_SAMESITE='Strict',
|
||||
SESSION_COOKIE_SAMESITE="Strict",
|
||||
)
|
||||
|
||||
csrf = CSRFProtect(app)
|
||||
#minify(app=app, html=True, js=True,cssless=False)
|
||||
minify(app=app, html=False, js=False,cssless=False)
|
||||
logger.debug(app.config)
|
||||
|
||||
#load config file
|
||||
with open('cfg/config.json') as json_data_file:
|
||||
if app.config["DEBUG"]:
|
||||
minify(app=app, html=False, js=False, cssless=False)
|
||||
else:
|
||||
minify(app=app, html=True, js=True, cssless=False)
|
||||
|
||||
# load config file
|
||||
with open("cfg/config.json") as json_data_file:
|
||||
cfg = json.load(json_data_file)
|
||||
|
||||
logging.debug("CFG:")
|
||||
logging.debug(cfg)
|
||||
#load bands file
|
||||
with open('cfg/bands.json') as json_bands:
|
||||
# load bands file
|
||||
with open("cfg/bands.json") as json_bands:
|
||||
band_frequencies = json.load(json_bands)
|
||||
|
||||
#load mode file
|
||||
with open('cfg/modes.json') as json_modes:
|
||||
# load mode file
|
||||
with open("cfg/modes.json") as json_modes:
|
||||
modes_frequencies = json.load(json_modes)
|
||||
|
||||
#load continents-cq file
|
||||
with open('cfg/continents.json') as json_continents:
|
||||
# load continents-cq file
|
||||
with open("cfg/continents.json") as json_continents:
|
||||
continents_cq = json.load(json_continents)
|
||||
|
||||
#read and set default for enabling cq filter
|
||||
if cfg.get('enable_cq_filter'):
|
||||
enable_cq_filter=cfg['enable_cq_filter'].upper()
|
||||
# read and set default for enabling cq filter
|
||||
if cfg.get("enable_cq_filter"):
|
||||
enable_cq_filter = cfg["enable_cq_filter"].upper()
|
||||
else:
|
||||
enable_cq_filter='N'
|
||||
enable_cq_filter = "N"
|
||||
|
||||
#define country table for search info on callsigns
|
||||
pfxt=prefix_table()
|
||||
# define country table for search info on callsigns
|
||||
pfxt = prefix_table()
|
||||
|
||||
#create object query manager
|
||||
qm=query_manager()
|
||||
# create object query manager
|
||||
qm = query_manager()
|
||||
|
||||
#find id in json : ie frequency / continent
|
||||
# find id in json : ie frequency / continent
|
||||
def find_id_json(json_object, name):
|
||||
return [obj for obj in json_object if obj['id']==name][0]
|
||||
return [obj for obj in json_object if obj["id"] == name][0]
|
||||
|
||||
|
||||
def query_build_callsign(callsign):
|
||||
|
||||
query_string=''
|
||||
if len(callsign)<=14:
|
||||
query_string="(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotter='"+callsign+"'"
|
||||
query_string+=" ORDER BY rowid desc limit 10)"
|
||||
query_string+=" UNION "
|
||||
query_string+="(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotcall='"+callsign+"'"
|
||||
query_string+=" ORDER BY rowid desc limit 10);"
|
||||
query_string = ""
|
||||
if len(callsign) <= 14:
|
||||
query_string = (
|
||||
"(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotter='"
|
||||
+ callsign
|
||||
+ "'"
|
||||
)
|
||||
query_string += " ORDER BY rowid desc limit 10)"
|
||||
query_string += " UNION "
|
||||
query_string += (
|
||||
"(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotcall='"
|
||||
+ callsign
|
||||
+ "'"
|
||||
)
|
||||
query_string += " ORDER BY rowid desc limit 10);"
|
||||
else:
|
||||
logging.warning('callsign too long')
|
||||
logging.warning("callsign too long")
|
||||
return query_string
|
||||
|
||||
|
||||
def query_build():
|
||||
|
||||
try:
|
||||
#get url parameters
|
||||
last_rowid=request.args.get('lr') #Last rowid fetched by front end
|
||||
band=(request.args.getlist('b')) #band filter
|
||||
dere=(request.args.getlist('e')) #DE continent filter
|
||||
dxre=(request.args.getlist('x')) #Dx continent filter
|
||||
mode=(request.args.getlist('m')) #mode filter
|
||||
decq=(request.args.getlist('qe')) #DE cq zone filter
|
||||
dxcq=(request.args.getlist('qx')) #DX cq zone filter
|
||||
# get url parameters
|
||||
last_rowid = request.args.get("lr") # Last rowid fetched by front end
|
||||
band = request.args.getlist("b") # band filter
|
||||
dere = request.args.getlist("e") # DE continent filter
|
||||
dxre = request.args.getlist("x") # Dx continent filter
|
||||
mode = request.args.getlist("m") # mode filter
|
||||
decq = request.args.getlist("qe") # DE cq zone filter
|
||||
dxcq = request.args.getlist("qx") # DX cq zone filter
|
||||
|
||||
query_string = ""
|
||||
|
||||
query_string=''
|
||||
|
||||
#construct band query decoding frequencies with json file
|
||||
band_qry_string = ' AND (('
|
||||
# construct band query decoding frequencies with json file
|
||||
band_qry_string = " AND (("
|
||||
for i, item_band in enumerate(band):
|
||||
freq=find_id_json(band_frequencies["bands"],item_band)
|
||||
freq = find_id_json(band_frequencies["bands"], item_band)
|
||||
if i > 0:
|
||||
band_qry_string += ') OR ('
|
||||
band_qry_string += ") OR ("
|
||||
|
||||
band_qry_string += 'freq BETWEEN ' + str(freq["min"]) + ' AND ' + str(freq["max"])
|
||||
band_qry_string += (
|
||||
"freq BETWEEN " + str(freq["min"]) + " AND " + str(freq["max"])
|
||||
)
|
||||
|
||||
band_qry_string += '))'
|
||||
band_qry_string += "))"
|
||||
|
||||
#construct mode query
|
||||
mode_qry_string = ' AND (('
|
||||
for i,item_mode in enumerate(mode):
|
||||
single_mode=find_id_json(modes_frequencies["modes"],item_mode)
|
||||
# construct mode query
|
||||
mode_qry_string = " AND (("
|
||||
for i, item_mode in enumerate(mode):
|
||||
single_mode = find_id_json(modes_frequencies["modes"], item_mode)
|
||||
if i > 0:
|
||||
mode_qry_string +=') OR ('
|
||||
mode_qry_string += ") OR ("
|
||||
for j in range(len(single_mode["freq"])):
|
||||
if j > 0:
|
||||
mode_qry_string +=') OR ('
|
||||
mode_qry_string += 'freq BETWEEN ' +str(single_mode["freq"][j]["min"]) + ' AND ' + str(single_mode["freq"][j]["max"])
|
||||
mode_qry_string += ") OR ("
|
||||
mode_qry_string += (
|
||||
"freq BETWEEN "
|
||||
+ str(single_mode["freq"][j]["min"])
|
||||
+ " AND "
|
||||
+ str(single_mode["freq"][j]["max"])
|
||||
)
|
||||
|
||||
mode_qry_string += '))'
|
||||
mode_qry_string += "))"
|
||||
|
||||
#construct DE continent region query
|
||||
dere_qry_string = ' AND spottercq IN ('
|
||||
# construct DE continent region query
|
||||
dere_qry_string = " AND spottercq IN ("
|
||||
for i, item_dere in enumerate(dere):
|
||||
continent=find_id_json(continents_cq["continents"],item_dere)
|
||||
continent = find_id_json(continents_cq["continents"], item_dere)
|
||||
if i > 0:
|
||||
dere_qry_string +=','
|
||||
dere_qry_string += ","
|
||||
dere_qry_string += str(continent["cq"])
|
||||
dere_qry_string +=')'
|
||||
dere_qry_string += ")"
|
||||
|
||||
#construct DX continent region query
|
||||
dxre_qry_string = ' AND spotcq IN ('
|
||||
# construct DX continent region query
|
||||
dxre_qry_string = " AND spotcq IN ("
|
||||
for i, item_dxre in enumerate(dxre):
|
||||
continent=find_id_json(continents_cq["continents"],item_dxre)
|
||||
continent = find_id_json(continents_cq["continents"], item_dxre)
|
||||
if i > 0:
|
||||
dxre_qry_string +=','
|
||||
dxre_qry_string += ","
|
||||
dxre_qry_string += str(continent["cq"])
|
||||
dxre_qry_string +=')'
|
||||
dxre_qry_string += ")"
|
||||
|
||||
if enable_cq_filter == 'Y':
|
||||
#construct de cq query
|
||||
decq_qry_string = ''
|
||||
if len(decq)==1:
|
||||
if enable_cq_filter == "Y":
|
||||
# construct de cq query
|
||||
decq_qry_string = ""
|
||||
if len(decq) == 1:
|
||||
if decq[0].isnumeric():
|
||||
decq_qry_string = ' AND spottercq =' + decq[0]
|
||||
#construct dx cq query
|
||||
dxcq_qry_string = ''
|
||||
if len(dxcq)==1:
|
||||
decq_qry_string = " AND spottercq =" + decq[0]
|
||||
# construct dx cq query
|
||||
dxcq_qry_string = ""
|
||||
if len(dxcq) == 1:
|
||||
if dxcq[0].isnumeric():
|
||||
dxcq_qry_string = ' AND spotcq =' + dxcq[0]
|
||||
dxcq_qry_string = " AND spotcq =" + dxcq[0]
|
||||
|
||||
if last_rowid is None:
|
||||
last_rowid = "0"
|
||||
if not last_rowid.isnumeric():
|
||||
last_rowid = 0
|
||||
|
||||
query_string="SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE rowid > "+last_rowid
|
||||
query_string = (
|
||||
"SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE rowid > "
|
||||
+ last_rowid
|
||||
)
|
||||
|
||||
if len(band) > 0:
|
||||
query_string += band_qry_string
|
||||
@ -170,7 +192,7 @@ def query_build():
|
||||
if len(dxre) > 0:
|
||||
query_string += dxre_qry_string
|
||||
|
||||
if enable_cq_filter == 'Y':
|
||||
if enable_cq_filter == "Y":
|
||||
if len(decq_qry_string) > 0:
|
||||
query_string += decq_qry_string
|
||||
|
||||
@ -181,42 +203,43 @@ def query_build():
|
||||
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
query_string = ''
|
||||
query_string = ""
|
||||
|
||||
return query_string
|
||||
|
||||
#the main query to show spots
|
||||
#it gets url parameter in order to apply the build the right query
|
||||
#and apply the filter required. It returns a json with the spots
|
||||
|
||||
# the main query to show spots
|
||||
# it gets url parameter in order to apply the build the right query
|
||||
# and apply the filter required. It returns a json with the spots
|
||||
def spotquery():
|
||||
try:
|
||||
|
||||
callsign=request.args.get('c') #search specific callsign
|
||||
callsign = request.args.get("c") # search specific callsign
|
||||
|
||||
if callsign:
|
||||
query_string=query_build_callsign(callsign)
|
||||
query_string = query_build_callsign(callsign)
|
||||
else:
|
||||
query_string=query_build()
|
||||
query_string = query_build()
|
||||
|
||||
qm.qry(query_string)
|
||||
data=qm.get_data()
|
||||
row_headers=qm.get_headers()
|
||||
data = qm.get_data()
|
||||
row_headers = qm.get_headers()
|
||||
|
||||
logger.debug("query done")
|
||||
logger.debug (data)
|
||||
logger.debug(data)
|
||||
|
||||
if data is None or len(data)==0:
|
||||
if data is None or len(data) == 0:
|
||||
logger.warning("no data found")
|
||||
|
||||
payload=[]
|
||||
payload = []
|
||||
for result in data:
|
||||
# create dictionary from recorset
|
||||
main_result=dict(zip(row_headers,result))
|
||||
main_result = dict(zip(row_headers, result))
|
||||
# find the country in prefix table
|
||||
search_prefix=pfxt.find(main_result["dx"])
|
||||
search_prefix = pfxt.find(main_result["dx"])
|
||||
# merge recordset and contry prefix
|
||||
main_result["country"]=search_prefix["country"]
|
||||
main_result["iso"]=search_prefix["iso"]
|
||||
main_result["country"] = search_prefix["country"]
|
||||
main_result["iso"] = search_prefix["iso"]
|
||||
|
||||
payload.append({**main_result})
|
||||
|
||||
@ -224,151 +247,228 @@ def spotquery():
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
#find adxo events
|
||||
adxo_events=None
|
||||
|
||||
# find adxo events
|
||||
adxo_events = None
|
||||
|
||||
|
||||
def get_adxo():
|
||||
global adxo_events
|
||||
adxo_events=get_adxo_events()
|
||||
threading.Timer(12*3600,get_adxo).start()
|
||||
adxo_events = get_adxo_events()
|
||||
threading.Timer(12 * 3600, get_adxo).start()
|
||||
|
||||
|
||||
get_adxo()
|
||||
|
||||
#create data provider for charts
|
||||
heatmap_cbp=ContinentsBandsProvider(logger,qm,continents_cq,band_frequencies)
|
||||
bar_graph_spm=SpotsPerMounthProvider(logger,qm)
|
||||
line_graph_st=SpotsTrend(logger,qm)
|
||||
bubble_graph_hb=HourBand(logger,qm,band_frequencies)
|
||||
geo_graph_wdsl=WorldDxSpotsLive(logger,qm,pfxt)
|
||||
# create data provider for charts
|
||||
heatmap_cbp = ContinentsBandsProvider(logger, qm, continents_cq, band_frequencies)
|
||||
bar_graph_spm = SpotsPerMounthProvider(logger, qm)
|
||||
line_graph_st = SpotsTrend(logger, qm)
|
||||
bubble_graph_hb = HourBand(logger, qm, band_frequencies)
|
||||
geo_graph_wdsl = WorldDxSpotsLive(logger, qm, pfxt)
|
||||
|
||||
#ROUTINGS
|
||||
@app.route('/spotlist', methods=['GET'])
|
||||
# ROUTINGS
|
||||
@app.route("/spotlist", methods=["GET"])
|
||||
def spotlist():
|
||||
response=flask.Response(json.dumps(spotquery()))
|
||||
response = flask.Response(json.dumps(spotquery()))
|
||||
return response
|
||||
|
||||
|
||||
def who_is_connected():
|
||||
host_port=cfg['telnet'].split(':')
|
||||
response=who(host_port[0],host_port[1],cfg['mycallsign'])
|
||||
host_port = cfg["telnet"].split(":")
|
||||
response = who(host_port[0], host_port[1], cfg["mycallsign"])
|
||||
return response
|
||||
|
||||
@app.route('/', methods=['GET'])
|
||||
@app.route('/index.html', methods=['GET'])
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
@app.route("/index.html", methods=["GET"])
|
||||
def spots():
|
||||
#payload=spotquery()
|
||||
response=flask.Response(render_template('index.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list'],enable_cq_filter=enable_cq_filter,timer_interval=cfg['timer']['interval'],adxo_events=adxo_events,continents=continents_cq,bands=band_frequencies))
|
||||
# payload=spotquery()
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"index.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
enable_cq_filter=enable_cq_filter,
|
||||
timer_interval=cfg["timer"]["interval"],
|
||||
adxo_events=adxo_events,
|
||||
continents=continents_cq,
|
||||
bands=band_frequencies,
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
@app.route('/service-worker.js', methods=['GET'])
|
||||
|
||||
@app.route("/service-worker.js", methods=["GET"])
|
||||
def sw():
|
||||
return app.send_static_file('service-worker.js')
|
||||
return app.send_static_file("service-worker.js")
|
||||
|
||||
@app.route('/offline.html')
|
||||
|
||||
@app.route("/offline.html")
|
||||
def root():
|
||||
return app.send_static_file('html/offline.html')
|
||||
return app.send_static_file("html/rel/offline.html")
|
||||
|
||||
@app.route('/world.json')
|
||||
|
||||
@app.route("/world.json")
|
||||
def world_data():
|
||||
return app.send_static_file('data/world.json')
|
||||
return app.send_static_file("data/world.json")
|
||||
|
||||
@app.route('/plots.html')
|
||||
|
||||
@app.route("/plots.html")
|
||||
def plots():
|
||||
whoj=who_is_connected()
|
||||
response=flask.Response(render_template('plots.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list'],who=whoj,continents=continents_cq,bands=band_frequencies))
|
||||
whoj = who_is_connected()
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"plots.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
who=whoj,
|
||||
continents=continents_cq,
|
||||
bands=band_frequencies,
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/cookies.html', methods=['GET'])
|
||||
@app.route("/cookies.html", methods=["GET"])
|
||||
def cookies():
|
||||
response=flask.Response(render_template('cookies.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list']))
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"cookies.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
@app.route('/privacy.html', methods=['GET'])
|
||||
|
||||
@app.route("/privacy.html", methods=["GET"])
|
||||
def privacy():
|
||||
response=flask.Response(render_template('privacy.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list']))
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"privacy.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
@app.route('/sitemap.xml')
|
||||
|
||||
@app.route("/sitemap.xml")
|
||||
def sitemap():
|
||||
return app.send_static_file('sitemap.xml')
|
||||
return app.send_static_file("sitemap.xml")
|
||||
|
||||
@app.route('/callsign.html', methods=['GET'])
|
||||
|
||||
@app.route("/callsign.html", methods=["GET"])
|
||||
def callsign():
|
||||
#payload=spotquery()
|
||||
callsign=request.args.get('c')
|
||||
response=flask.Response(render_template('callsign.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list'],timer_interval=cfg['timer']['interval'],callsign=callsign,adxo_events=adxo_events,continents=continents_cq,bands=band_frequencies))
|
||||
# payload=spotquery()
|
||||
callsign = request.args.get("c")
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"callsign.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
timer_interval=cfg["timer"]["interval"],
|
||||
callsign=callsign,
|
||||
adxo_events=adxo_events,
|
||||
continents=continents_cq,
|
||||
bands=band_frequencies,
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
#API that search a callsign and return all informations about that
|
||||
@app.route('/callsign', methods=['GET'])
|
||||
|
||||
# API that search a callsign and return all informations about that
|
||||
@app.route("/callsign", methods=["GET"])
|
||||
def find_callsign():
|
||||
callsign=request.args.get('c')
|
||||
response=pfxt.find(callsign)
|
||||
callsign = request.args.get("c")
|
||||
response = pfxt.find(callsign)
|
||||
if response is None:
|
||||
response=flask.Response(status=204)
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_heatmap_data', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_heatmap_data", methods=["GET"])
|
||||
def get_heatmap_data():
|
||||
continent=request.args.get('continent')
|
||||
response=flask.Response(json.dumps(heatmap_cbp.get_data(continent)))
|
||||
continent = request.args.get("continent")
|
||||
response = flask.Response(json.dumps(heatmap_cbp.get_data(continent)))
|
||||
logger.debug(response)
|
||||
if response is None:
|
||||
response=flask.Response(status=204)
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_dx_spots_per_month', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_dx_spots_per_month", methods=["GET"])
|
||||
def get_dx_spots_per_month():
|
||||
response=flask.Response(json.dumps(bar_graph_spm.get_data()))
|
||||
response = flask.Response(json.dumps(bar_graph_spm.get_data()))
|
||||
logger.debug(response)
|
||||
if response is None:
|
||||
response=flask.Response(status=204)
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_dx_spots_trend', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_dx_spots_trend", methods=["GET"])
|
||||
def get_dx_spots_trend():
|
||||
response=flask.Response(json.dumps(line_graph_st.get_data()))
|
||||
response = flask.Response(json.dumps(line_graph_st.get_data()))
|
||||
logger.debug(response)
|
||||
if response is None:
|
||||
response=flask.Response(status=204)
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_hour_band', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_hour_band", methods=["GET"])
|
||||
def get_dx_hour_band():
|
||||
response=flask.Response(json.dumps(bubble_graph_hb.get_data()))
|
||||
response = flask.Response(json.dumps(bubble_graph_hb.get_data()))
|
||||
logger.debug(response)
|
||||
if response is None:
|
||||
response=flask.Response(status=204)
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_world_dx_spots_live', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_world_dx_spots_live", methods=["GET"])
|
||||
def get_world_dx_spots_live():
|
||||
response=flask.Response(json.dumps(geo_graph_wdsl.get_data()))
|
||||
response = flask.Response(json.dumps(geo_graph_wdsl.get_data()))
|
||||
logger.debug(response)
|
||||
if response is None:
|
||||
response=flask.Response(status=204)
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def inject_template_scope():
|
||||
injections = dict()
|
||||
|
||||
def cookies_check():
|
||||
value = request.cookies.get('cookie_consent')
|
||||
return value == 'true'
|
||||
value = request.cookies.get("cookie_consent")
|
||||
return value == "true"
|
||||
|
||||
injections.update(cookies_check=cookies_check)
|
||||
return injections
|
||||
|
||||
|
||||
@app.after_request
|
||||
def add_security_headers(resp):
|
||||
resp.headers['Strict-Transport-Security']='max-age=1000'
|
||||
resp.headers['X-Xss-Protection']='1; mode=block'
|
||||
resp.headers['X-Frame-Options']='SAMEORIGIN'
|
||||
resp.headers['X-Content-Type-Options']='nosniff'
|
||||
resp.headers['Referrer-Policy']='strict-origin-when-cross-origin'
|
||||
resp.headers['Cache-Control']='public, no-cache'
|
||||
resp.headers['Pragma']='no-cache'
|
||||
resp.headers["Strict-Transport-Security"] = "max-age=1000"
|
||||
resp.headers["X-Xss-Protection"] = "1; mode=block"
|
||||
resp.headers["X-Frame-Options"] = "SAMEORIGIN"
|
||||
resp.headers["X-Content-Type-Options"] = "nosniff"
|
||||
resp.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
||||
resp.headers["Cache-Control"] = "public, no-cache"
|
||||
resp.headers["Pragma"] = "no-cache"
|
||||
|
||||
resp.headers['Content-Security-Policy']="\
|
||||
resp.headers[
|
||||
"Content-Security-Policy"
|
||||
] = "\
|
||||
default-src 'self';\
|
||||
script-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
||||
style-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
||||
@ -384,10 +484,10 @@ def add_security_headers(resp):
|
||||
"
|
||||
return resp
|
||||
|
||||
|
||||
# style-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx' 'sha512-uvXdJud8WaOlQFjlz9B15Yy2Au/bMAvz79F7Xa6OakCl2jvQPdHD0hb3dEqZRdSwG4/sknePXlE7GiarwA/9Wg==';\
|
||||
# style-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
||||
|
||||
#script-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline'
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0')
|
||||
|
||||
# script-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline'
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0")
|
||||
|
Loading…
Reference in New Issue
Block a user