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
|
config.json
|
||||||
__init__
|
__init__
|
||||||
__pycache__
|
__pycache__
|
||||||
|
.vscode/
|
||||||
|
61
README.md
61
README.md
@ -13,7 +13,7 @@
|
|||||||
- **Release:** v2.4.1
|
- **Release:** v2.4.1
|
||||||
- **Author:** Corrado Gerbaldo - [IU1BOW](https://www.qrz.com/db/IU1BOW)
|
- **Author:** Corrado Gerbaldo - [IU1BOW](https://www.qrz.com/db/IU1BOW)
|
||||||
- **Mail:** <corrado.gerbaldo@gmail.com>
|
- **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
|
- **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
|
### API
|
||||||
**Spot list**
|
**Spot list**
|
||||||
|
|
||||||
You can retrive last spots calling "**/spotlist**"; For example [www.iu1bow.com/spotlist](https://www.iu1bow.com/spotlist)
|
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)
|
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
|
### Screenshots
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -8,10 +8,11 @@ keys=stream_handler,file_handler
|
|||||||
keys=formatter
|
keys=formatter
|
||||||
|
|
||||||
[logger_root]
|
[logger_root]
|
||||||
level=INFO
|
level=INFO
|
||||||
handlers=stream_handler,file_handler
|
handlers=stream_handler,file_handler
|
||||||
|
|
||||||
[handler_stream_handler]
|
[handler_stream_handler]
|
||||||
|
level=INFO
|
||||||
class=StreamHandler
|
class=StreamHandler
|
||||||
formatter=formatter
|
formatter=formatter
|
||||||
args=(sys.stderr,)
|
args=(sys.stderr,)
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
### Change log
|
### Change log
|
||||||
Date: 02/01/2023
|
Date: 07/01/2023
|
||||||
Release v.2.4
|
Release: v2.4.1
|
||||||
- changed dimensions of spots in world dx spost charts
|
- changed dimensions of spots in world dx spost charts
|
||||||
- managed empty data in data providers for charts
|
- managed empty data in data providers for charts
|
||||||
- removed jQuery: migrated to vanilla javascript
|
- removed jQuery: migrated to vanilla javascript
|
||||||
- for spot refresh get only new spots starting from last rowid
|
- for spot refresh get only new spots starting from last rowid
|
||||||
|
- modified building script
|
||||||
|
- moved cty file in data directory
|
||||||
___
|
___
|
||||||
Date: 01/01/2023
|
Date: 01/01/2023
|
||||||
Release v.2.4
|
Release: v2.4
|
||||||
- migration to python 3.11
|
- migration to python 3.11
|
||||||
- added descriptions to continents
|
- added descriptions to continents
|
||||||
- fixed issue #23: Wrong flag for Cocos (Keeling) Islands
|
- fixed issue #23: Wrong flag for Cocos (Keeling) Islands
|
||||||
|
108
lib/adxo.py
108
lib/adxo.py
@ -1,78 +1,86 @@
|
|||||||
#***********************************************************************************
|
# ***********************************************************************************
|
||||||
# Module used to get Announced DX Operation from NG3K website via .ICS (Calendar)
|
# Module used to get Announced DX Operation from NG3K website via .ICS (Calendar)
|
||||||
# file, parse it and return a dictionary with these events
|
# file, parse it and return a dictionary with these events
|
||||||
#***********************************************************************************
|
# ***********************************************************************************
|
||||||
__author__ = 'IU1BOW - Corrado'
|
__author__ = "IU1BOW - Corrado"
|
||||||
import requests
|
import requests
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
logging.basicConfig(
|
||||||
#format single line
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||||
|
datefmt="%m/%d/%Y %I:%M:%S",
|
||||||
|
)
|
||||||
|
# format single line
|
||||||
def format_line(prop):
|
def format_line(prop):
|
||||||
prop_out=dict()
|
prop_out = dict()
|
||||||
try:
|
try:
|
||||||
dtstart=datetime.strptime(prop['DTSTART;VALUE=DATE'], '%Y%m%d')
|
dtstart = datetime.strptime(prop["DTSTART;VALUE=DATE"], "%Y%m%d")
|
||||||
dtend=datetime.strptime(prop['DTEND;VALUE=DATE'], '%Y%m%d')
|
dtend = datetime.strptime(prop["DTEND;VALUE=DATE"], "%Y%m%d")
|
||||||
now=datetime.now()
|
now = datetime.now()
|
||||||
if dtstart <=now and dtend>=now:
|
if dtstart <= now and dtend >= now:
|
||||||
prop_out['start']=dtstart.strftime('%Y-%m-%dT%H:%M:%S%z')
|
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["end"] = dtend.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||||
prop_out['summary']=prop['SUMMARY'].split('(')[0].strip()
|
prop_out["summary"] = prop["SUMMARY"].split("(")[0].strip()
|
||||||
prop_out['callsign']=prop['SUMMARY'].split('(',1)[1].split(')',1)[0]
|
prop_out["callsign"] = prop["SUMMARY"].split("(", 1)[1].split(")", 1)[0]
|
||||||
prop_out['description']=prop['DESCRIPTION'].replace('\\', '')
|
prop_out["description"] = prop["DESCRIPTION"].replace("\\", "")
|
||||||
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return prop_out
|
return prop_out
|
||||||
|
|
||||||
#TODO: url from conf parameter
|
|
||||||
|
# TODO: url from conf parameter
|
||||||
|
|
||||||
|
|
||||||
def get_adxo_events():
|
def get_adxo_events():
|
||||||
url = 'http://dxcal.kj4z.com/dxcal'
|
url = "http://dxcal.kj4z.com/dxcal"
|
||||||
line_num=0
|
line_num = 0
|
||||||
event_num=0
|
event_num = 0
|
||||||
try:
|
try:
|
||||||
logging.info('connection to: '+url)
|
logging.info("connection to: " + url)
|
||||||
req=requests.get(url)
|
req = requests.get(url)
|
||||||
events=[]
|
events = []
|
||||||
prop=dict()
|
prop = dict()
|
||||||
prop_name=''
|
prop_name = ""
|
||||||
with tempfile.TemporaryFile() as temp:
|
with tempfile.TemporaryFile() as temp:
|
||||||
temp.write(req.content)
|
temp.write(req.content)
|
||||||
temp.seek(0)
|
temp.seek(0)
|
||||||
lines=temp.readlines()
|
lines = temp.readlines()
|
||||||
for line_bytes in lines:
|
for line_bytes in lines:
|
||||||
line=line_bytes.decode()
|
line = line_bytes.decode()
|
||||||
line_num+=1
|
line_num += 1
|
||||||
current_line_array=line.strip().split(':', 1)
|
current_line_array = line.strip().split(":", 1)
|
||||||
if current_line_array[0]=='BEGIN':
|
if current_line_array[0] == "BEGIN":
|
||||||
if current_line_array[1]=='VCALENDAR':
|
if current_line_array[1] == "VCALENDAR":
|
||||||
prop={}
|
prop = {}
|
||||||
if current_line_array[1]=='VEVENT':
|
if current_line_array[1] == "VEVENT":
|
||||||
event_num+=1
|
event_num += 1
|
||||||
prop={}
|
prop = {}
|
||||||
else:
|
else:
|
||||||
if current_line_array[0]=='END':
|
if current_line_array[0] == "END":
|
||||||
if current_line_array[1]=='VCALENDAR':
|
if current_line_array[1] == "VCALENDAR":
|
||||||
pass
|
pass
|
||||||
if current_line_array[1]=='VEVENT':
|
if current_line_array[1] == "VEVENT":
|
||||||
prop=format_line(prop)
|
prop = format_line(prop)
|
||||||
if prop:
|
if prop:
|
||||||
events.append(prop)
|
events.append(prop)
|
||||||
else:
|
else:
|
||||||
if len(current_line_array)>1:
|
if len(current_line_array) > 1:
|
||||||
prop_name=current_line_array[0]
|
prop_name = current_line_array[0]
|
||||||
prop[prop_name]=current_line_array[1]
|
prop[prop_name] = current_line_array[1]
|
||||||
else:
|
else:
|
||||||
if len(prop_name)>0:
|
if len(prop_name) > 0:
|
||||||
prop[prop_name]=prop[prop_name]+current_line_array[0]
|
prop[prop_name] = (
|
||||||
|
prop[prop_name] + current_line_array[0]
|
||||||
|
)
|
||||||
|
|
||||||
logging.debug('number of line reads: '+str(line_num))
|
logging.debug("number of line reads: " + str(line_num))
|
||||||
logging.info('number ADXO events: '+str(event_num))
|
logging.info("number ADXO events: " + str(event_num))
|
||||||
return events
|
return events
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
logging.error(e1)
|
logging.error(e1)
|
||||||
return
|
return
|
||||||
|
377
lib/config.py
377
lib/config.py
@ -1,55 +1,59 @@
|
|||||||
#*************************************************************************************
|
# *************************************************************************************
|
||||||
# CLI Utility used for manage configuration file
|
# CLI Utility used for manage configuration file
|
||||||
#*************************************************************************************
|
# *************************************************************************************
|
||||||
__author__ = 'IU1BOW - Corrado'
|
__author__ = "IU1BOW - Corrado"
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
from os import path
|
from os import path
|
||||||
import json
|
import json
|
||||||
|
|
||||||
configs = [('mycallsign','Callsign_______________: '),
|
configs = [
|
||||||
('mysql/host','MySql host_____________: '),
|
("mycallsign", "Callsign_______________: "),
|
||||||
('mysql/db','MySql database_________: '),
|
("mysql/host", "MySql host_____________: "),
|
||||||
('mysql/user','MySql user_____________: '),
|
("mysql/db", "MySql database_________: "),
|
||||||
('mysql/passwd','MySql password_________: '),
|
("mysql/user", "MySql user_____________: "),
|
||||||
('timer/interval','Spot page refresh(ms)__: ' ),
|
("mysql/passwd", "MySql password_________: "),
|
||||||
('plot_refresh_timer/interval','Plot page refresh(ms)__: '),
|
("timer/interval", "Spot page refresh(ms)__: "),
|
||||||
('mail','Mail address___________: '),
|
("plot_refresh_timer/interval", "Plot page refresh(ms)__: "),
|
||||||
('mail_token','token google 2FA auth__: '),
|
("mail", "Mail address___________: "),
|
||||||
('telnet','Telnet address_________: '),
|
("mail_token", "token google 2FA auth__: "),
|
||||||
('enable_cq_filter','Enable cq filter______: ')
|
("telnet", "Telnet address_________: "),
|
||||||
]
|
("enable_cq_filter", "Enable cq filter______: "),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class bcolors:
|
class bcolors:
|
||||||
HEADER = '\033[95m'
|
HEADER = "\033[95m"
|
||||||
OKBLUE = '\033[94m'
|
OKBLUE = "\033[94m"
|
||||||
OKGREEN = '\033[92m'
|
OKGREEN = "\033[92m"
|
||||||
WARNING = '\033[93m'
|
WARNING = "\033[93m"
|
||||||
FAIL = '\033[91m'
|
FAIL = "\033[91m"
|
||||||
ENDC = '\033[0m'
|
ENDC = "\033[0m"
|
||||||
BOLD = '\033[1m'
|
BOLD = "\033[1m"
|
||||||
UNDERLINE = '\033[4m'
|
UNDERLINE = "\033[4m"
|
||||||
|
|
||||||
TEMPLATE_FILE = '../cfg/config.json.template'
|
|
||||||
USER_FILE = '../cfg/config.json'
|
TEMPLATE_FILE = "../cfg/config.json.template"
|
||||||
#search and open the configuration file
|
USER_FILE = "../cfg/config.json"
|
||||||
|
# search and open the configuration file
|
||||||
def get_cfg_file(template):
|
def get_cfg_file(template):
|
||||||
if template:
|
if template:
|
||||||
cfg_file = TEMPLATE_FILE
|
cfg_file = TEMPLATE_FILE
|
||||||
if not path.exists(cfg_file):
|
if not path.exists(cfg_file):
|
||||||
print ('file not found')
|
print("file not found")
|
||||||
cfg_file = ''
|
cfg_file = ""
|
||||||
else:
|
else:
|
||||||
cfg_file = USER_FILE
|
cfg_file = USER_FILE
|
||||||
if not path.exists(cfg_file):
|
if not path.exists(cfg_file):
|
||||||
cfg_file = TEMPLATE_FILE
|
cfg_file = TEMPLATE_FILE
|
||||||
if not path.exists(cfg_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
|
return cfg_file
|
||||||
|
|
||||||
#covert file in json
|
|
||||||
|
# covert file in json
|
||||||
def get_cfg_json(f):
|
def get_cfg_json(f):
|
||||||
if f:
|
if f:
|
||||||
with open(f) as json_data_file:
|
with open(f) as json_data_file:
|
||||||
@ -59,10 +63,11 @@ def get_cfg_json(f):
|
|||||||
|
|
||||||
return cfg
|
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)
|
l_arr = len(k_arr)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -71,228 +76,254 @@ def get_cfg_value(cfg,key):
|
|||||||
elif l_arr == 2:
|
elif l_arr == 2:
|
||||||
val = cfg[k_arr[0]][k_arr[1]]
|
val = cfg[k_arr[0]][k_arr[1]]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
val=''
|
val = ""
|
||||||
|
|
||||||
return 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)
|
l_arr = len(k_arr)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if l_arr == 1:
|
if l_arr == 1:
|
||||||
cfg[k_arr[0]]=val
|
cfg[k_arr[0]] = val
|
||||||
elif l_arr == 2:
|
elif l_arr == 2:
|
||||||
cfg[k_arr[0]][k_arr[1]]=val
|
cfg[k_arr[0]][k_arr[1]] = val
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
def style_field(lbl,val):
|
|
||||||
return lbl+bcolors.BOLD + str(val) + bcolors.ENDC
|
|
||||||
|
|
||||||
def show_menu(cfg,key):
|
def style_field(lbl, val):
|
||||||
menu=get_cfg_value(cfg,'menu/menu_list')
|
return lbl + bcolors.BOLD + str(val) + bcolors.ENDC
|
||||||
i=-1
|
|
||||||
|
|
||||||
|
def show_menu(cfg, key):
|
||||||
|
menu = get_cfg_value(cfg, "menu/menu_list")
|
||||||
|
i = -1
|
||||||
for element in menu:
|
for element in menu:
|
||||||
i+=1
|
i += 1
|
||||||
print(style_field(str(i)+'. external: ',str(element['external']))+style_field(', label: ',element['label']))
|
print(
|
||||||
print(style_field(' link____: ',element['link']))
|
style_field(str(i) + ". external: ", str(element["external"]))
|
||||||
print()
|
+ style_field(", label: ", element["label"])
|
||||||
|
)
|
||||||
|
print(style_field(" link____: ", element["link"]))
|
||||||
|
print()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def help_list():
|
def help_list():
|
||||||
print ()
|
print()
|
||||||
print (' h: help')
|
print(" h: help")
|
||||||
print (' vc: view config.')
|
print(" vc: view config.")
|
||||||
print (' ec: edit config.')
|
print(" ec: edit config.")
|
||||||
print (' vm: view menu')
|
print(" vm: view menu")
|
||||||
print (' em: edit menu')
|
print(" em: edit menu")
|
||||||
print (' s: save')
|
print(" s: save")
|
||||||
print (' t: load config. from template')
|
print(" t: load config. from template")
|
||||||
print ()
|
print()
|
||||||
print (' x: exit')
|
print(" x: exit")
|
||||||
print ()
|
print()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def help_menu_edit():
|
def help_menu_edit():
|
||||||
print ()
|
print()
|
||||||
print (' n: new menu entry')
|
print(" n: new menu entry")
|
||||||
print (' d: delete menu entry')
|
print(" d: delete menu entry")
|
||||||
print (' e: edit menu entry')
|
print(" e: edit menu entry")
|
||||||
print ()
|
print()
|
||||||
print (' x: exit')
|
print(" x: exit")
|
||||||
print ()
|
print()
|
||||||
return
|
return
|
||||||
|
|
||||||
def view(cfg,t):
|
|
||||||
if t == 'c':
|
def view(cfg, t):
|
||||||
i=0
|
if t == "c":
|
||||||
|
i = 0
|
||||||
for element in configs:
|
for element in configs:
|
||||||
(key, lbl)=element
|
(key, lbl) = element
|
||||||
print(style_field(str(i)+'. '+lbl,str(get_cfg_value(cfg,key))))
|
print(style_field(str(i) + ". " + lbl, str(get_cfg_value(cfg, key))))
|
||||||
i+=1
|
i += 1
|
||||||
elif t == 'm':
|
elif t == "m":
|
||||||
print ('Menu:')
|
print("Menu:")
|
||||||
show_menu(cfg,'menu/menu_list')
|
show_menu(cfg, "menu/menu_list")
|
||||||
|
|
||||||
print()
|
print()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def user_input(caption):
|
def user_input(caption):
|
||||||
return input(caption)
|
return input(caption)
|
||||||
|
|
||||||
|
|
||||||
def edit_config(cfg):
|
def edit_config(cfg):
|
||||||
view(cfg,'c')
|
view(cfg, "c")
|
||||||
inp=''
|
inp = ""
|
||||||
while inp!='x':
|
while inp != "x":
|
||||||
inp=str(user_input ('Type the number of config. you would to edit, x for end: ')).lower()
|
inp = str(
|
||||||
|
user_input("Type the number of config. you would to edit, x for end: ")
|
||||||
|
).lower()
|
||||||
if inp.isdigit():
|
if inp.isdigit():
|
||||||
inp=int(inp)
|
inp = int(inp)
|
||||||
try:
|
try:
|
||||||
(key, lbl)=configs[inp]
|
(key, lbl) = configs[inp]
|
||||||
print(style_field(lbl,get_cfg_value(cfg,key)))
|
print(style_field(lbl, get_cfg_value(cfg, key)))
|
||||||
val=str(user_input ('Enter new value, [ENTER] for nothing: '))
|
val = str(user_input("Enter new value, [ENTER] for nothing: "))
|
||||||
except IndexError:
|
except IndexError:
|
||||||
print ('configuration not found!')
|
print("configuration not found!")
|
||||||
finally:
|
finally:
|
||||||
if val!='x' and val !='':
|
if val != "x" and val != "":
|
||||||
cfg=set_cfg_value(cfg,key,val)
|
cfg = set_cfg_value(cfg, key, val)
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
def menu_delete_entry(cfg):
|
def menu_delete_entry(cfg):
|
||||||
view(cfg,'m')
|
view(cfg, "m")
|
||||||
inp=''
|
inp = ""
|
||||||
while inp!='x':
|
while inp != "x":
|
||||||
inp=str(user_input ('Choose the menu you would to delete, x for end: ')).lower()
|
inp = str(
|
||||||
|
user_input("Choose the menu you would to delete, x for end: ")
|
||||||
|
).lower()
|
||||||
if inp.isdigit():
|
if inp.isdigit():
|
||||||
inp=int(inp)
|
inp = int(inp)
|
||||||
element = cfg['menu']['menu_list']
|
element = cfg["menu"]["menu_list"]
|
||||||
try:
|
try:
|
||||||
del element[inp]
|
del element[inp]
|
||||||
cfg['menu']['menu_list']=element
|
cfg["menu"]["menu_list"] = element
|
||||||
except IndexError:
|
except IndexError:
|
||||||
print ('menu entry not found!')
|
print("menu entry not found!")
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
def is_external(val):
|
def is_external(val):
|
||||||
return val == 'y'
|
return val == "y"
|
||||||
|
|
||||||
def menu_input_entry(entry,new_entry):
|
|
||||||
if not new_entry:
|
|
||||||
print('label old value: '+entry['label'])
|
|
||||||
|
|
||||||
label=str(user_input('label new value: '))
|
|
||||||
if not new_entry:
|
|
||||||
print('link old value: '+entry['link'])
|
|
||||||
|
|
||||||
link=str(user_input('link value: '))
|
|
||||||
if not new_entry:
|
|
||||||
print('external old value: '+str(entry['external']))
|
|
||||||
|
|
||||||
external = ''
|
|
||||||
while external != 'y' and external != 'n':
|
|
||||||
external=str(user_input('open link external [y/n]: ')).lower()
|
|
||||||
|
|
||||||
# if external == 'y':
|
def menu_input_entry(entry, new_entry):
|
||||||
# external = True
|
if not new_entry:
|
||||||
# else:
|
print("label old value: " + entry["label"])
|
||||||
# external = False
|
|
||||||
external=is_external(external)
|
|
||||||
|
|
||||||
entry['label']=label
|
label = str(user_input("label new value: "))
|
||||||
entry['link']=link
|
if not new_entry:
|
||||||
entry['external']=external
|
print("link old value: " + entry["link"])
|
||||||
|
|
||||||
|
link = str(user_input("link value: "))
|
||||||
|
if not new_entry:
|
||||||
|
print("external old value: " + str(entry["external"]))
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
entry["label"] = label
|
||||||
|
entry["link"] = link
|
||||||
|
entry["external"] = external
|
||||||
|
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
|
|
||||||
def menu_edit_entry(cfg):
|
def menu_edit_entry(cfg):
|
||||||
view(cfg,'m')
|
view(cfg, "m")
|
||||||
inp=''
|
inp = ""
|
||||||
while inp!='x':
|
while inp != "x":
|
||||||
inp=str(user_input ('Choose the menu you would to edit, X for end: ')).lower()
|
inp = str(user_input("Choose the menu you would to edit, X for end: ")).lower()
|
||||||
if inp.isdigit():
|
if inp.isdigit():
|
||||||
inp=int(inp)
|
inp = int(inp)
|
||||||
element = cfg['menu']['menu_list']
|
element = cfg["menu"]["menu_list"]
|
||||||
try:
|
try:
|
||||||
element[inp]=menu_input_entry(element[inp],False)
|
element[inp] = menu_input_entry(element[inp], False)
|
||||||
cfg['menu']['menu_list']=element
|
cfg["menu"]["menu_list"] = element
|
||||||
except IndexError:
|
except IndexError:
|
||||||
print ('menu entry not found!')
|
print("menu entry not found!")
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
def menu_new_entry(cfg):
|
def menu_new_entry(cfg):
|
||||||
view(cfg,'m')
|
view(cfg, "m")
|
||||||
inp=''
|
inp = ""
|
||||||
valid = False
|
valid = False
|
||||||
entry=menu_input_entry({"label":"", "link": "", "external": False},True)
|
entry = menu_input_entry({"label": "", "link": "", "external": False}, True)
|
||||||
while not valid:
|
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():
|
if inp.isdigit():
|
||||||
inp=int(inp)
|
inp = int(inp)
|
||||||
if inp > len(cfg['menu']['menu_list']):
|
if inp > len(cfg["menu"]["menu_list"]):
|
||||||
print('position not valid!')
|
print("position not valid!")
|
||||||
valid = False
|
valid = False
|
||||||
elif inp == len(cfg['menu']['menu_list']):
|
elif inp == len(cfg["menu"]["menu_list"]):
|
||||||
cfg['menu']['menu_list'].append(entry)
|
cfg["menu"]["menu_list"].append(entry)
|
||||||
valid = True
|
valid = True
|
||||||
else:
|
else:
|
||||||
cfg['menu']['menu_list'].insert(inp,entry)
|
cfg["menu"]["menu_list"].insert(inp, entry)
|
||||||
valid = True
|
valid = True
|
||||||
else:
|
else:
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
def edit_menu(cfg):
|
def edit_menu(cfg):
|
||||||
view(cfg,'m')
|
view(cfg, "m")
|
||||||
inp=''
|
inp = ""
|
||||||
while inp!='x':
|
while inp != "x":
|
||||||
help_menu_edit()
|
help_menu_edit()
|
||||||
inp=str(user_input ('Edit menu> make your choiche: ')).lower()
|
inp = str(user_input("Edit menu> make your choiche: ")).lower()
|
||||||
if (inp == 'n'):
|
if inp == "n":
|
||||||
cfg=menu_new_entry(cfg)
|
cfg = menu_new_entry(cfg)
|
||||||
elif inp == 'd':
|
elif inp == "d":
|
||||||
cfg=menu_delete_entry(cfg)
|
cfg = menu_delete_entry(cfg)
|
||||||
elif inp == 'e':
|
elif inp == "e":
|
||||||
cfg=menu_edit_entry(cfg)
|
cfg = menu_edit_entry(cfg)
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
def save_cfg(cfg):
|
def save_cfg(cfg):
|
||||||
with open(USER_FILE, 'w') as outfile:
|
with open(USER_FILE, "w") as outfile:
|
||||||
json.dump(cfg, outfile,indent=4)
|
json.dump(cfg, outfile, indent=4)
|
||||||
print ('configuration saved to: '+USER_FILE)
|
print("configuration saved to: " + USER_FILE)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print()
|
print()
|
||||||
print('*** DxSpider configuration ***')
|
print("*** DxSpider configuration ***")
|
||||||
finput=get_cfg_file(False)
|
finput = get_cfg_file(False)
|
||||||
help_list()
|
help_list()
|
||||||
cfg=get_cfg_json(finput)
|
cfg = get_cfg_json(finput)
|
||||||
inp = ''
|
inp = ""
|
||||||
while inp != 'x' and inp != 'exit':
|
while inp != "x" and inp != "exit":
|
||||||
inp = str(user_input('Main> make your choiche: ')).lower()
|
inp = str(user_input("Main> make your choiche: ")).lower()
|
||||||
if (inp == 'h' or inp =='?' or inp =='help'):
|
if inp == "h" or inp == "?" or inp == "help":
|
||||||
help_list()
|
help_list()
|
||||||
elif inp == 'vc':
|
elif inp == "vc":
|
||||||
view(cfg,'c')
|
view(cfg, "c")
|
||||||
elif inp == 'vm':
|
elif inp == "vm":
|
||||||
view(cfg,'m')
|
view(cfg, "m")
|
||||||
elif inp == 'ec':
|
elif inp == "ec":
|
||||||
cfg=edit_config(cfg)
|
cfg = edit_config(cfg)
|
||||||
elif inp == 'em':
|
elif inp == "em":
|
||||||
cfg=edit_menu(cfg)
|
cfg = edit_menu(cfg)
|
||||||
elif inp == 's' or inp == 'save':
|
elif inp == "s" or inp == "save":
|
||||||
save_cfg(cfg)
|
save_cfg(cfg)
|
||||||
elif inp == 't':
|
elif inp == "t":
|
||||||
finput=get_cfg_file(True)
|
finput = get_cfg_file(True)
|
||||||
cfg=get_cfg_json(finput)
|
cfg = get_cfg_json(finput)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
419
lib/cty.py
419
lib/cty.py
@ -1,8 +1,8 @@
|
|||||||
#*************************************************************************************
|
# *************************************************************************************
|
||||||
# Module used to download cty.dat country file and search callsign in it
|
# Module used to download cty.dat country file and search callsign in it
|
||||||
#*************************************************************************************
|
# *************************************************************************************
|
||||||
__author__ = 'IU1BOW - Corrado'
|
__author__ = "IU1BOW - Corrado"
|
||||||
import requests
|
import requests
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
@ -10,124 +10,147 @@ from threading import Timer
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import json
|
import json
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
logging.basicConfig(
|
||||||
#TODO: url from conf parameter
|
level=logging.INFO,
|
||||||
url = 'https://www.country-files.com/cty/cty_wt_mod.dat'
|
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||||
cty_local=os.path.dirname(__file__)+'/../cfg/cty_wt_mod.dat'
|
datefmt="%m/%d/%Y %I:%M:%S",
|
||||||
country_file=os.path.dirname(__file__)+'/../cfg/country.json'
|
)
|
||||||
#-------------------------------------------------------------------------------------
|
# 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
|
# download country files cty.dat
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def download_cty(url,cty_local):
|
def download_cty(url, cty_local):
|
||||||
try:
|
try:
|
||||||
logging.info('connection to: '+url)
|
logging.info("connection to: " + url)
|
||||||
req=requests.get(url)
|
req = requests.get(url)
|
||||||
f=open(cty_local,'wb')
|
f = open(cty_local, "wb")
|
||||||
f.write(req.content)
|
f.write(req.content)
|
||||||
f.close()
|
f.close()
|
||||||
logging.info('cty file saved in: '+cty_local)
|
logging.info("cty file saved in: " + cty_local)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
logging.error(e1)
|
logging.error(e1)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
# get age of a file in days
|
# -------------------------------------------------------------------------------------
|
||||||
#-------------------------------------------------------------------------------------
|
# get age of a file in days
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
def file_age_in_days(pathname):
|
def file_age_in_days(pathname):
|
||||||
return (time.time() - os.stat(pathname).st_ctime)/(24*3600)
|
return (time.time() - os.stat(pathname).st_ctime) / (24 * 3600)
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
# manage file cty.dat
|
# manage file cty.dat
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def get_cty(url,local):
|
def get_cty(url, local):
|
||||||
if os.path.isfile(local):
|
if os.path.isfile(local):
|
||||||
age=file_age_in_days(local)
|
age = file_age_in_days(local)
|
||||||
if age>7:
|
if age > 7:
|
||||||
logging.info(cty_local+' too old ('+str(round(age,0))+' days): proceding to download it')
|
logging.info(
|
||||||
return download_cty(url,local)
|
cty_local
|
||||||
# else:
|
+ " too old ("
|
||||||
# logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary to download it')
|
+ str(round(age, 0))
|
||||||
# return 0
|
+ " days): proceding to download it"
|
||||||
logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary 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
|
return 0
|
||||||
# else:
|
# else:
|
||||||
# logging.info(cty_local+' not present: proceding to download it')
|
# logging.info(cty_local+' not present: proceding to download it')
|
||||||
# return download_cty(url,local)
|
# return download_cty(url,local)
|
||||||
|
|
||||||
logging.info(cty_local+' not present: proceding to download it')
|
logging.info(cty_local + " not present: proceding to download it")
|
||||||
return download_cty(url,local)
|
return download_cty(url, local)
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
# parsing alias and get exceptions
|
# parsing alias and get exceptions
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def parse_alias(alias,master):
|
def parse_alias(alias, master):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
#create a dictionary of array, with start and end position of each exception
|
# create a dictionary of array, with start and end position of each exception
|
||||||
find_dict={}
|
find_dict = {}
|
||||||
find_dict['pos_cq']=[alias.find('('),alias.find(')')]
|
find_dict["pos_cq"] = [alias.find("("), alias.find(")")]
|
||||||
find_dict['pos_itu']=[alias.find('['),alias.find(']')]
|
find_dict["pos_itu"] = [alias.find("["), alias.find("]")]
|
||||||
find_dict['pos_lat_lon']=[alias.find('<'),alias.find('>')]
|
find_dict["pos_lat_lon"] = [alias.find("<"), alias.find(">")]
|
||||||
find_dict['pos_continent']=[alias.find('{'),alias.find('}')]
|
find_dict["pos_continent"] = [alias.find("{"), alias.find("}")]
|
||||||
find_dict['pos_time']=[alias.find('~'),alias[:alias.find('~')].find('~')]
|
find_dict["pos_time"] = [alias.find("~"), alias[: alias.find("~")].find("~")]
|
||||||
|
|
||||||
first=9999
|
first = 9999
|
||||||
parsed={}
|
parsed = {}
|
||||||
|
|
||||||
#assign default values from master callsing
|
# assign default values from master callsing
|
||||||
parsed["country"]=master["country"]
|
parsed["country"] = master["country"]
|
||||||
parsed["cq"]=master["cq"]
|
parsed["cq"] = master["cq"]
|
||||||
parsed["itu"]=master["itu"]
|
parsed["itu"] = master["itu"]
|
||||||
parsed["continent"]=master["continent"]
|
parsed["continent"] = master["continent"]
|
||||||
parsed["lat"]=master["lat"]
|
parsed["lat"] = master["lat"]
|
||||||
parsed["lon"]=master["lon"]
|
parsed["lon"] = master["lon"]
|
||||||
parsed["time_loc"]=master["time_loc"]
|
parsed["time_loc"] = master["time_loc"]
|
||||||
parsed["full"]=master["full"]
|
parsed["full"] = master["full"]
|
||||||
parsed["darc_waedc"]=master["darc_waedc"]
|
parsed["darc_waedc"] = master["darc_waedc"]
|
||||||
|
|
||||||
#extract override cq
|
# extract override cq
|
||||||
if find_dict['pos_cq'][0]>=0:
|
if find_dict["pos_cq"][0] >= 0:
|
||||||
parsed["cq"]=alias[find_dict['pos_cq'][0]+1:find_dict['pos_cq'][1]]
|
parsed["cq"] = alias[find_dict["pos_cq"][0] + 1 : find_dict["pos_cq"][1]]
|
||||||
if find_dict['pos_cq'][0] < first:
|
if find_dict["pos_cq"][0] < first:
|
||||||
first=find_dict['pos_cq'][0]
|
first = find_dict["pos_cq"][0]
|
||||||
|
|
||||||
#extract override itu
|
# extract override itu
|
||||||
if find_dict['pos_itu'][0]>=0:
|
if find_dict["pos_itu"][0] >= 0:
|
||||||
parsed["itu"]=alias[find_dict['pos_itu'][0]+1:find_dict['pos_itu'][1]]
|
parsed["itu"] = alias[find_dict["pos_itu"][0] + 1 : find_dict["pos_itu"][1]]
|
||||||
if find_dict['pos_itu'][0] < first:
|
if find_dict["pos_itu"][0] < first:
|
||||||
first=find_dict['pos_itu'][0]
|
first = find_dict["pos_itu"][0]
|
||||||
|
|
||||||
#extract override lat_lon
|
# extract override lat_lon
|
||||||
if find_dict['pos_lat_lon'][0]>=0:
|
if find_dict["pos_lat_lon"][0] >= 0:
|
||||||
lat_lon=alias[find_dict['pos_lat_lon'][0]+1:find_dict['pos_lat_lon'][1]]
|
lat_lon = alias[
|
||||||
parsed["lat"]=lat_lon[0:].split('/')[0]
|
find_dict["pos_lat_lon"][0] + 1 : find_dict["pos_lat_lon"][1]
|
||||||
parsed["lon"]=lat_lon[:len(lat_lon)].split('/')[1]
|
]
|
||||||
if find_dict['pos_lat_lon'][0] < first:
|
parsed["lat"] = lat_lon[0:].split("/")[0]
|
||||||
first=find_dict['pos_lat_lon'][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
|
# extract override continent
|
||||||
if find_dict['pos_continent'][0]>=0:
|
if find_dict["pos_continent"][0] >= 0:
|
||||||
parsed["continent"]=alias[find_dict['pos_continent'][0]+1:find_dict['pos_continent'][1]]
|
parsed["continent"] = alias[
|
||||||
if find_dict['pos_continent'][0] < first:
|
find_dict["pos_continent"][0] + 1 : find_dict["pos_continent"][1]
|
||||||
first=find_dict['pos_continent'][0]
|
]
|
||||||
|
if find_dict["pos_continent"][0] < first:
|
||||||
|
first = find_dict["pos_continent"][0]
|
||||||
|
|
||||||
#extract override time
|
# extract override time
|
||||||
if find_dict['pos_time'][0]>=0:
|
if find_dict["pos_time"][0] >= 0:
|
||||||
parsed["time_loc"]=alias[find_dict['pos_time'][0]+1:find_dict['pos_time'][1]]
|
parsed["time_loc"] = alias[
|
||||||
if find_dict['pos_time'][0] < first:
|
find_dict["pos_time"][0] + 1 : find_dict["pos_time"][1]
|
||||||
first=find_dict['pos_time'][0]
|
]
|
||||||
|
if find_dict["pos_time"][0] < first:
|
||||||
|
first = find_dict["pos_time"][0]
|
||||||
|
|
||||||
#extract callsign
|
# extract callsign
|
||||||
callsing=alias[:first].upper()
|
callsing = alias[:first].upper()
|
||||||
if callsing.startswith('='):
|
if callsing.startswith("="):
|
||||||
parsed["full"]='y'
|
parsed["full"] = "y"
|
||||||
callsing=callsing[1:]
|
callsing = callsing[1:]
|
||||||
|
|
||||||
if callsing.startswith('*'):
|
if callsing.startswith("*"):
|
||||||
parsed["darc_waedc"]='y'
|
parsed["darc_waedc"] = "y"
|
||||||
callsing=callsing[1:]
|
callsing = callsing[1:]
|
||||||
|
|
||||||
return callsing, parsed
|
return callsing, parsed
|
||||||
|
|
||||||
@ -138,43 +161,47 @@ def parse_alias(alias,master):
|
|||||||
logging.error(alias)
|
logging.error(alias)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
# load file from configuration, containing all world country, with related ISO codes
|
# load file from configuration, containing all world country, with related ISO codes
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def load_country():
|
def load_country():
|
||||||
with open(country_file) as json_country:
|
with open(country_file) as json_country:
|
||||||
return json.load(json_country)
|
return json.load(json_country)
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
# search for ISO code, transcoding the country description
|
# search for ISO code, transcoding the country description
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def add_country(table):
|
def add_country(table):
|
||||||
country_data=load_country()
|
country_data = load_country()
|
||||||
for key, value in table.items():
|
for key, value in table.items():
|
||||||
found=0
|
found = 0
|
||||||
for i in country_data['country_codes']:
|
for i in country_data["country_codes"]:
|
||||||
if i['desc'].upper()==value['country'].upper():
|
if i["desc"].upper() == value["country"].upper():
|
||||||
value["iso"]=i["ISO"]
|
value["iso"] = i["ISO"]
|
||||||
value["wpx"]=i["WPX"]
|
value["wpx"] = i["WPX"]
|
||||||
found=1
|
found = 1
|
||||||
break
|
break
|
||||||
if found==0:
|
if found == 0:
|
||||||
logging.warning('country "'+value['country']+'" not found in cfg/country.json')
|
logging.warning(
|
||||||
|
'country "' + value["country"] + '" not found in cfg/country.json'
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
class prefix_table:
|
class prefix_table:
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
# init of the class
|
# init of the class
|
||||||
# - download file cty.dat
|
# - download file cty.dat
|
||||||
# - parse file and create prefix_master with all prefixies and attributes
|
# - parse file and create prefix_master with all prefixies and attributes
|
||||||
#.....................................................................................
|
# .....................................................................................
|
||||||
# CTY.DAT Format
|
# CTY.DAT Format
|
||||||
#
|
#
|
||||||
# reference: https://www.country-files.com/cty-dat-format/
|
# reference: https://www.country-files.com/cty-dat-format/
|
||||||
#
|
#
|
||||||
# Note that the fields are aligned in columns and spaced out for readability only. It
|
# Note that the fields are aligned in columns and spaced out for readability only. It
|
||||||
# is the “:” at the end of each field that acts as a delimiter for that field:
|
# is the “:” at the end of each field that acts as a delimiter for that field:
|
||||||
# Column Length Description
|
# Column Length Description
|
||||||
# 1 26 Country Name
|
# 1 26 Country Name
|
||||||
@ -185,17 +212,17 @@ class prefix_table:
|
|||||||
# 51 10 Longitude in degrees, + for West
|
# 51 10 Longitude in degrees, + for West
|
||||||
# 61 9 Local time offset from GMT
|
# 61 9 Local time offset from GMT
|
||||||
# 70 6 Primary DXCC Prefix (A “*” preceding this prefix indicates that the country
|
# 70 6 Primary DXCC Prefix (A “*” preceding this prefix indicates that the country
|
||||||
# is on the DARC WAEDC list, and counts in CQ-sponsored contests, but not
|
# is on the DARC WAEDC list, and counts in CQ-sponsored contests, but not
|
||||||
# ARRL-sponsored contests).
|
# ARRL-sponsored contests).
|
||||||
#
|
#
|
||||||
# Alias DXCC prefixes (including the primary one) follow on consecutive lines,
|
# Alias DXCC prefixes (including the primary one) follow on consecutive lines,
|
||||||
# separated by commas (,). Multiple lines are OK; a line to be continued should end with
|
# separated by commas (,). Multiple lines are OK; a line to be continued should end with
|
||||||
# comma (,) though it’s not required. A semi-colon (;) terminates the last alias
|
# comma (,) though it’s not required. A semi-colon (;) terminates the last alias
|
||||||
# prefix in the list.
|
# prefix in the list.
|
||||||
#
|
#
|
||||||
# If an alias prefix is preceded by ‘=’, this indicates that the prefix is to be treated
|
# If an alias prefix is preceded by ‘=’, this indicates that the prefix is to be treated
|
||||||
# as a full callsign, i.e. must be an exact match.
|
# as a full callsign, i.e. must be an exact match.
|
||||||
#
|
#
|
||||||
# The following special characters can be applied after an alias prefix:
|
# The following special characters can be applied after an alias prefix:
|
||||||
# (#) Override CQ Zone
|
# (#) Override CQ Zone
|
||||||
# [#] Override ITU Zone
|
# [#] Override ITU Zone
|
||||||
@ -203,123 +230,127 @@ class prefix_table:
|
|||||||
# {aa} Override Continent
|
# {aa} Override Continent
|
||||||
# ~#~ Override local time offset from GMT
|
# ~#~ Override local time offset from GMT
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
global prefix_master
|
global prefix_master
|
||||||
prefix_master=dict()
|
prefix_master = dict()
|
||||||
initialization()
|
initialization()
|
||||||
return
|
return
|
||||||
|
|
||||||
global initialization
|
global initialization
|
||||||
|
|
||||||
def initialization():
|
def initialization():
|
||||||
refresh()
|
refresh()
|
||||||
global timer
|
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()
|
timer.start()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
#-------------------------------------------------------------------------------------
|
# refresh data
|
||||||
# refresh data
|
# -------------------------------------------------------------------------------------
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
global refresh
|
global refresh
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
|
|
||||||
logging.info('CTY: start initialization')
|
logging.info("CTY: start initialization")
|
||||||
if get_cty(url,cty_local)>0:
|
if get_cty(url, cty_local) > 0:
|
||||||
logging.error('there is a problem during downloading country files!')
|
logging.error("there is a problem during downloading country files!")
|
||||||
logging.info('continue with previous file')
|
logging.info("continue with previous file")
|
||||||
logging.info('check the connectivity, or put manually the file '+cty_local)
|
logging.info(
|
||||||
line_num=0
|
"check the connectivity, or put manually the file " + cty_local
|
||||||
line_num_valid=0
|
)
|
||||||
entities_number=0
|
line_num = 0
|
||||||
data=''
|
line_num_valid = 0
|
||||||
table=[]
|
entities_number = 0
|
||||||
|
data = ""
|
||||||
|
table = []
|
||||||
prefix_master.clear()
|
prefix_master.clear()
|
||||||
try:
|
try:
|
||||||
with open(cty_local, 'r') as f:
|
with open(cty_local, "r") as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
line_num+=1
|
line_num += 1
|
||||||
li=line.strip()
|
li = line.strip()
|
||||||
#remove comments
|
# remove comments
|
||||||
if not li.startswith("#"):
|
if not li.startswith("#"):
|
||||||
line_num_valid+=1
|
line_num_valid += 1
|
||||||
data+=li
|
data += li
|
||||||
logging.info('number of lines reads: '+str(line_num))
|
logging.info("number of lines reads: " + str(line_num))
|
||||||
logging.info('number of valid lines: '+str(line_num_valid))
|
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(',')
|
|
||||||
for sb in sub_prefixies:
|
|
||||||
values={}
|
|
||||||
callsign, values=parse_alias(sb,single_prefix)
|
|
||||||
prefix_master[callsign]=values
|
|
||||||
|
|
||||||
logging.info('number of entities: '+str(entities_number))
|
# split in array of lines terminated with semicolon
|
||||||
logging.info('number of single alias: '+str(len(prefix_master)))
|
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
|
||||||
|
|
||||||
|
logging.info("number of entities: " + str(entities_number))
|
||||||
|
logging.info("number of single alias: " + str(len(prefix_master)))
|
||||||
add_country(prefix_master)
|
add_country(prefix_master)
|
||||||
logging.info('memory used for prefix: '+str(prefix_master.__sizeof__())+' bytes')
|
logging.info(
|
||||||
logging.info('CTY: initialization complete')
|
"memory used for prefix: " + str(prefix_master.__sizeof__()) + " bytes"
|
||||||
|
)
|
||||||
|
logging.info("CTY: initialization complete")
|
||||||
return
|
return
|
||||||
|
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
|
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
|
||||||
message = template.format(type(e1).__name__, e1.args)
|
message = template.format(type(e1).__name__, e1.args)
|
||||||
logging.error(message)
|
logging.error(message)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
# find a callsign
|
# find a callsign
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def find(self,callsign):
|
def find(self, callsign):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data=dict()
|
data = dict()
|
||||||
i=len(callsign)
|
i = len(callsign)
|
||||||
callsign=callsign.strip().upper()
|
callsign = callsign.strip().upper()
|
||||||
while i>0:
|
while i > 0:
|
||||||
try:
|
try:
|
||||||
data=prefix_master[callsign[:i]]
|
data = prefix_master[callsign[:i]]
|
||||||
data['match']=callsign[:i]
|
data["match"] = callsign[:i]
|
||||||
return data
|
return data
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
i-=1
|
i -= 1
|
||||||
|
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
|
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
|
||||||
message = template.format(type(e1).__name__, e1.args)
|
message = template.format(type(e1).__name__, e1.args)
|
||||||
logging.error(message)
|
logging.error(message)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
#not found
|
# not found
|
||||||
data["country"]="unknown country"
|
data["country"] = "unknown country"
|
||||||
data["iso"]="xx"
|
data["iso"] = "xx"
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
logging.info('prefix_table destroyed')
|
logging.info("prefix_table destroyed")
|
||||||
return
|
return
|
||||||
|
@ -1,74 +1,78 @@
|
|||||||
#*************************************************************************************
|
# *************************************************************************************
|
||||||
# Module used to interface with telnet cluster and get connected nodes
|
# Module used to interface with telnet cluster and get connected nodes
|
||||||
#*************************************************************************************
|
# *************************************************************************************
|
||||||
__author__ = 'IU1BOW - Corrado'
|
__author__ = "IU1BOW - Corrado"
|
||||||
|
|
||||||
import telnetlib
|
import telnetlib
|
||||||
import struct
|
import struct
|
||||||
import json
|
import json
|
||||||
import logging
|
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()
|
lines = lines.splitlines()
|
||||||
fmtstring='2x 9s 10s 18s 9s 8s 15s'
|
fmtstring = "2x 9s 10s 18s 9s 8s 15s"
|
||||||
fieldstruct = struct.Struct(fmtstring)
|
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
|
# skip first lines and last line
|
||||||
payload=[]
|
payload = []
|
||||||
for i in range(3,len(lines)-1):
|
for i in range(3, len(lines) - 1):
|
||||||
line=lines[i]
|
line = lines[i]
|
||||||
ln=len(line)
|
ln = len(line)
|
||||||
|
|
||||||
padding=bytes(' ' * (struct.calcsize(fmtstring)-ln),'utf-8')
|
padding = bytes(" " * (struct.calcsize(fmtstring) - ln), "utf-8")
|
||||||
line=(line+padding)
|
line = line + padding
|
||||||
|
|
||||||
if ln > 10:
|
if ln > 10:
|
||||||
parse = fieldstruct.unpack_from
|
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:
|
try:
|
||||||
fields[j]=item_field.decode('utf-8').strip()
|
fields[j] = item_field.decode("utf-8").strip()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print(item_field)
|
print(item_field)
|
||||||
payload.append(dict(zip(row_headers,fields)))
|
payload.append(dict(zip(row_headers, fields)))
|
||||||
|
|
||||||
# payload = json.dumps(payload)
|
# payload = json.dumps(payload)
|
||||||
|
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
def who(host,port,user):
|
|
||||||
|
|
||||||
WAIT_FOR = b'dxspider >'
|
def who(host, port, user):
|
||||||
TIMEOUT=1
|
|
||||||
res=0
|
WAIT_FOR = b"dxspider >"
|
||||||
|
TIMEOUT = 1
|
||||||
|
res = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tn = telnetlib.Telnet(host,port,TIMEOUT)
|
tn = telnetlib.Telnet(host, port, TIMEOUT)
|
||||||
try:
|
try:
|
||||||
tn.read_until(b"login: ",TIMEOUT)
|
tn.read_until(b"login: ", TIMEOUT)
|
||||||
tn.write(user.encode('ascii') + b"\n")
|
tn.write(user.encode("ascii") + b"\n")
|
||||||
res=tn.read_until(WAIT_FOR,TIMEOUT)
|
res = tn.read_until(WAIT_FOR, TIMEOUT)
|
||||||
tn.write(b"who\n")
|
tn.write(b"who\n")
|
||||||
res=tn.read_until(WAIT_FOR,TIMEOUT)
|
res = tn.read_until(WAIT_FOR, TIMEOUT)
|
||||||
tn.write(b"exit\n")
|
tn.write(b"exit\n")
|
||||||
|
|
||||||
except EOFError:
|
except EOFError:
|
||||||
logging.error ('could not autenticate to telnet dxspider host: check user callsign ')
|
logging.error(
|
||||||
logging.error (res)
|
"could not autenticate to telnet dxspider host: check user callsign "
|
||||||
res=0
|
)
|
||||||
|
logging.error(res)
|
||||||
|
res = 0
|
||||||
except:
|
except:
|
||||||
logging.error ('could not connect to telnet dxspider host: check host/port')
|
logging.error("could not connect to telnet dxspider host: check host/port")
|
||||||
ret = ''
|
ret = ""
|
||||||
|
|
||||||
if res!=0:
|
if res != 0:
|
||||||
ret=parse_who(res)
|
ret = parse_who(res)
|
||||||
else:
|
else:
|
||||||
ret=''
|
ret = ""
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#*************************************************************************************
|
# *************************************************************************************
|
||||||
# Module used to convert dxcluster band file to band configuration file modes for
|
# Module used to convert dxcluster band file to band configuration file modes for
|
||||||
# spiderweb
|
# spiderweb
|
||||||
# you can use it in build step
|
# you can use it in build step
|
||||||
#*************************************************************************************
|
# *************************************************************************************
|
||||||
__author__ = 'IU1BOW - Corrado'
|
__author__ = "IU1BOW - Corrado"
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
@ -12,47 +12,53 @@ import json
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
logging.basicConfig(
|
||||||
#dxspider_band='/home/sysop/spider/data/bands.pl'
|
level=logging.INFO,
|
||||||
output_modes='../cfg/modes.json'
|
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
|
# reads bands file and convert to json
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def parse(input_file):
|
def parse(input_file):
|
||||||
line_num=0
|
line_num = 0
|
||||||
line_num_valid=0
|
line_num_valid = 0
|
||||||
data=''
|
data = ""
|
||||||
band_trigger = False
|
band_trigger = False
|
||||||
try:
|
try:
|
||||||
with open(input_file, 'r') as f:
|
with open(input_file, "r") as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
line_num+=1
|
line_num += 1
|
||||||
li=line.strip()
|
li = line.strip()
|
||||||
#remove comments
|
# remove comments
|
||||||
if li.startswith("%bands = "):
|
if li.startswith("%bands = "):
|
||||||
band_trigger = True
|
band_trigger = True
|
||||||
if li.endswith(");"):
|
if li.endswith(");"):
|
||||||
band_trigger = False
|
band_trigger = False
|
||||||
if not li.startswith("#") and band_trigger == True:
|
if not li.startswith("#") and band_trigger == True:
|
||||||
line_num_valid+=1
|
line_num_valid += 1
|
||||||
data+=li
|
data += li
|
||||||
logging.debug("first step parsing output: ")
|
logging.debug("first step parsing output: ")
|
||||||
logging.debug(data)
|
logging.debug(data)
|
||||||
|
|
||||||
#replacing strings in order to obtain a json
|
# replacing strings in order to obtain a json
|
||||||
data=data.lower()
|
data = data.lower()
|
||||||
data=data.replace(" ","")
|
data = data.replace(" ", "")
|
||||||
data=data.replace("bless","")
|
data = data.replace("bless", "")
|
||||||
data=data.replace("%bands=","")
|
data = data.replace("%bands=", "")
|
||||||
data=data.replace(",'bands'","")
|
data = data.replace(",'bands'", "")
|
||||||
data=re.sub(r"([\"'])(?:(?=(\\?))\2.)*?\1=>", "", data) #remove token like '136khz' =>
|
data = re.sub(
|
||||||
data=data.replace("=>",":")
|
r"([\"'])(?:(?=(\\?))\2.)*?\1=>", "", data
|
||||||
data=data.replace("(","")
|
) # remove token like '136khz' =>
|
||||||
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.replace(",}","}")
|
data = data.replace(")", "")
|
||||||
data="["+data+"]"
|
data = re.sub(r"([a-zA-Z]+):", r'"\1":', data) # add quotation around words
|
||||||
data=data.replace(",]","]")
|
data = data.replace(",}", "}")
|
||||||
|
data = "[" + data + "]"
|
||||||
|
data = data.replace(",]", "]")
|
||||||
logging.debug("second step parsing output: ")
|
logging.debug("second step parsing output: ")
|
||||||
logging.debug(data)
|
logging.debug(data)
|
||||||
|
|
||||||
@ -65,18 +71,21 @@ def parse(input_file):
|
|||||||
logging.error(input_file)
|
logging.error(input_file)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
# add min max freq element to the related mode in final json
|
# add min max freq element to the related mode in final json
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def add_freq(mode, freq, json_modes):
|
def add_freq(mode, freq, json_modes):
|
||||||
try:
|
try:
|
||||||
for modes in json_modes['modes']:
|
for modes in json_modes["modes"]:
|
||||||
if modes["id"]==mode:
|
if modes["id"] == mode:
|
||||||
ind_freq = 0
|
ind_freq = 0
|
||||||
while ind_freq < len(freq):
|
while ind_freq < len(freq):
|
||||||
modes["freq"].append({"min":freq[ind_freq],"max":freq[ind_freq+1]})
|
modes["freq"].append(
|
||||||
ind_freq +=2
|
{"min": freq[ind_freq], "max": freq[ind_freq + 1]}
|
||||||
|
)
|
||||||
|
ind_freq += 2
|
||||||
|
|
||||||
return json_modes
|
return json_modes
|
||||||
|
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
@ -85,24 +94,27 @@ def add_freq(mode, freq, json_modes):
|
|||||||
logging.error(message)
|
logging.error(message)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------------
|
|
||||||
|
# -------------------------------------------------------------------------------------
|
||||||
# reads bands file and convert to json
|
# reads bands file and convert to json
|
||||||
#-------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------
|
||||||
def create_output(data):
|
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:
|
try:
|
||||||
for element in data:
|
for element in data:
|
||||||
for mode in element:
|
for mode in element:
|
||||||
if mode == "band":
|
if mode == "band":
|
||||||
pass
|
pass
|
||||||
elif mode == "cw":
|
elif mode == "cw":
|
||||||
json_modes=add_freq("cw",element[mode],json_modes)
|
json_modes = add_freq("cw", element[mode], json_modes)
|
||||||
elif mode == "ssb":
|
elif mode == "ssb":
|
||||||
json_modes=add_freq("phone",element[mode],json_modes)
|
json_modes = add_freq("phone", element[mode], json_modes)
|
||||||
else:
|
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("final step output: ")
|
||||||
logging.debug(json.dumps(json_modes))
|
logging.debug(json.dumps(json_modes))
|
||||||
@ -115,23 +127,24 @@ def create_output(data):
|
|||||||
logging.error(input_file)
|
logging.error(input_file)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
#*************************************************************************************
|
|
||||||
|
# *************************************************************************************
|
||||||
# Main
|
# Main
|
||||||
#*************************************************************************************
|
# *************************************************************************************
|
||||||
logging.info('RDxSpider band file conversion starting...')
|
logging.info("RDxSpider band file conversion starting...")
|
||||||
if len(sys.argv)!=2:
|
if len(sys.argv) != 2:
|
||||||
logging.error("argument invalid. Specify dxcluster band file")
|
logging.error("argument invalid. Specify dxcluster band file")
|
||||||
logging.error("use: python get_dxcluster_modes.py input_file")
|
logging.error("use: python get_dxcluster_modes.py input_file")
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
dxspider_band=sys.argv[1]
|
dxspider_band = sys.argv[1]
|
||||||
|
|
||||||
parsed = parse(dxspider_band)
|
parsed = parse(dxspider_band)
|
||||||
if parsed != "" :
|
if parsed != "":
|
||||||
json_output=create_output(parsed)
|
json_output = create_output(parsed)
|
||||||
with open(output_modes,'w') as outfile:
|
with open(output_modes, "w") as outfile:
|
||||||
json.dump(json_output, outfile,indent=4)
|
json.dump(json_output, outfile, indent=4)
|
||||||
logging.info('modes saved to: '+output_modes)
|
logging.info("modes saved to: " + output_modes)
|
||||||
logging.info('DxSpider band file conversion completed')
|
logging.info("DxSpider band file conversion completed")
|
||||||
else:
|
else:
|
||||||
logging.error("error on parsing input file")
|
logging.error("error on parsing input file")
|
||||||
|
@ -1,83 +1,101 @@
|
|||||||
#***********************************************************************************
|
# ***********************************************************************************
|
||||||
# Module that contain classess for providing data to plotting front-end page
|
# Module that contain classess for providing data to plotting front-end page
|
||||||
#***********************************************************************************
|
# ***********************************************************************************
|
||||||
__author__ = 'IU1BOW - Corrado'
|
__author__ = "IU1BOW - Corrado"
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from lib.qry import query_manager
|
from lib.qry import query_manager
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import json
|
import json
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------
|
||||||
# Base class (as template for other classes)
|
# Base class (as template for other classes)
|
||||||
#-----------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------
|
||||||
class BaseDataProvider:
|
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_data
|
||||||
global glb_last_refresh
|
global glb_last_refresh
|
||||||
global glb_response
|
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
|
# time for refresh
|
||||||
def refresh(self):
|
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 {}
|
||||||
|
|
||||||
#return data to the caller
|
|
||||||
def get_data(self):
|
|
||||||
self.glb_response = {}
|
|
||||||
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)
|
|
||||||
# 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.qm = qm
|
|
||||||
self.continents=continents
|
|
||||||
self.bands=bands
|
|
||||||
self.refresh()
|
|
||||||
self.logger.info("Class: %s init end",self.__class__.__name__)
|
|
||||||
return
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------
|
# return data to the caller
|
||||||
|
def get_data(self):
|
||||||
|
self.glb_response = {}
|
||||||
|
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)
|
||||||
|
# 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.qm = qm
|
||||||
|
self.continents = continents
|
||||||
|
self.bands = bands
|
||||||
|
self.refresh()
|
||||||
|
self.logger.info("Class: %s init end", self.__class__.__name__)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------
|
||||||
# Class for managing data for Continent/Band chart
|
# Class for managing data for Continent/Band chart
|
||||||
#-----------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------
|
||||||
class ContinentsBandsProvider(BaseDataProvider):
|
class ContinentsBandsProvider(BaseDataProvider):
|
||||||
|
def __init__(self, logger, qm, continents, bands):
|
||||||
|
# Calling constructor of base class
|
||||||
|
super().__init__(logger, qm, continents, bands)
|
||||||
|
|
||||||
def __init__(self,logger, qm, continents, bands):
|
def __load_data(self, band_frequencies, continents_cq):
|
||||||
# Calling constructor of base class
|
|
||||||
super().__init__(logger,qm,continents,bands)
|
|
||||||
|
|
||||||
def __load_data(self,band_frequencies, continents_cq):
|
self.logger.info("Start")
|
||||||
|
self.logger.info("doing query...")
|
||||||
|
|
||||||
self.logger.info("Start")
|
# construct bands query
|
||||||
self.logger.info("doing 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"] + '"'
|
||||||
|
|
||||||
#construct bands query
|
# construct continent region query
|
||||||
bands_qry_string = 'CASE '
|
spottercq_qry_string = "CASE "
|
||||||
self.logger.debug(band_frequencies)
|
spotcq_qry_string = "CASE "
|
||||||
for i in range(len(band_frequencies["bands"])):
|
for i in range(len(continents_cq["continents"])):
|
||||||
bands_qry_string+=' WHEN freq between '+str(band_frequencies["bands"][i]["min"])+' AND '+ str(band_frequencies["bands"][i]["max"])
|
spottercq_qry_string += (
|
||||||
bands_qry_string+=' THEN "'+band_frequencies["bands"][i]["id"]+'"'
|
" 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 continent region query
|
# construct final query string
|
||||||
spottercq_qry_string = 'CASE '
|
qry_string = (
|
||||||
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"]+'"'
|
|
||||||
|
|
||||||
#construct final query string
|
|
||||||
qry_string ="""
|
|
||||||
SELECT
|
SELECT
|
||||||
"""+spottercq_qry_string+""" ELSE spottercq END,
|
"""
|
||||||
"""+spotcq_qry_string+""" ELSE spotcq END,
|
+ spottercq_qry_string
|
||||||
"""+bands_qry_string+""" END,
|
+ """ ELSE spottercq END,
|
||||||
|
"""
|
||||||
|
+ spotcq_qry_string
|
||||||
|
+ """ ELSE spotcq END,
|
||||||
|
"""
|
||||||
|
+ bands_qry_string
|
||||||
|
+ """ END,
|
||||||
count(0) number
|
count(0) number
|
||||||
from spot
|
from spot
|
||||||
where
|
where
|
||||||
@ -86,91 +104,97 @@ class ContinentsBandsProvider(BaseDataProvider):
|
|||||||
group by 1, 2, 3
|
group by 1, 2, 3
|
||||||
;
|
;
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
self.logger.debug(qry_string)
|
self.logger.debug(qry_string)
|
||||||
self.qm.qry(qry_string)
|
self.qm.qry(qry_string)
|
||||||
data=self.qm.get_data()
|
data = self.qm.get_data()
|
||||||
if len(data)==0:
|
if len(data) == 0:
|
||||||
self.logger.warning("no data found")
|
self.logger.warning("no data found")
|
||||||
|
|
||||||
self.logger.info("query done")
|
self.logger.info("query done")
|
||||||
self.logger.debug (data)
|
self.logger.debug(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
# function for search continent in the global data returned by query and making a cartesian product
|
# function for search continent in the global data returned by query and making a cartesian product
|
||||||
# in order to prepare data for heatmap
|
# in order to prepare data for heatmap
|
||||||
def __normalize_continent(self,data_list,continent,continents_list, band_list):
|
def __normalize_continent(self, data_list, continent, continents_list, band_list):
|
||||||
data_filtered=[]
|
data_filtered = []
|
||||||
for i, item_data in enumerate(data_list):
|
for i, item_data in enumerate(data_list):
|
||||||
if item_data[0]==continent and not (item_data[3] is None):
|
if item_data[0] == continent and not (item_data[3] is None):
|
||||||
element=[]
|
element = []
|
||||||
element.append(item_data[1])
|
element.append(item_data[1])
|
||||||
element.append(item_data[2])
|
element.append(item_data[2])
|
||||||
element.append(item_data[3])
|
element.append(item_data[3])
|
||||||
data_filtered.append(element)
|
data_filtered.append(element)
|
||||||
|
|
||||||
cartesian_product = []
|
cartesian_product = []
|
||||||
|
|
||||||
for j, item_continent in enumerate(continents_list):
|
for j, item_continent in enumerate(continents_list):
|
||||||
for k, item_band in enumerate(band_list):
|
for k, item_band in enumerate(band_list):
|
||||||
found=0
|
found = 0
|
||||||
for lis, item_filtered in enumerate(data_filtered):
|
for lis, item_filtered in enumerate(data_filtered):
|
||||||
if item_filtered[0]==item_continent["id"] and item_filtered[1]==item_band["id"]:
|
if (
|
||||||
#cartesian_product.append(item_filtered)
|
item_filtered[0] == item_continent["id"]
|
||||||
element=[]
|
and item_filtered[1] == item_band["id"]
|
||||||
element.append(j)
|
):
|
||||||
element.append(k)
|
# cartesian_product.append(item_filtered)
|
||||||
element.append(item_filtered[2])
|
element = []
|
||||||
cartesian_product.append(element)
|
element.append(j)
|
||||||
found=1
|
element.append(k)
|
||||||
if found==0:
|
element.append(item_filtered[2])
|
||||||
element=[]
|
cartesian_product.append(element)
|
||||||
element.append(j)
|
found = 1
|
||||||
element.append(k)
|
if found == 0:
|
||||||
element.append(0)
|
element = []
|
||||||
cartesian_product.append(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)
|
self.logger.debug(cartesian_product)
|
||||||
return cartesian_product
|
return cartesian_product
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
super().refresh()
|
super().refresh()
|
||||||
lcl_data={}
|
lcl_data = {}
|
||||||
qry_data=self.__load_data(self.bands, self.continents)
|
qry_data = self.__load_data(self.bands, self.continents)
|
||||||
for i, item in enumerate(self.continents["continents"]):
|
for i, item in enumerate(self.continents["continents"]):
|
||||||
continent=item["id"]
|
continent = item["id"]
|
||||||
data_de=self.__normalize_continent(qry_data,continent,self.continents["continents"],self.bands["bands"])
|
data_de = self.__normalize_continent(
|
||||||
lcl_data.update({continent:data_de})
|
qry_data, continent, self.continents["continents"], self.bands["bands"]
|
||||||
|
)
|
||||||
self.glb_data=lcl_data
|
lcl_data.update({continent: data_de})
|
||||||
self.glb_last_refresh=time.time()
|
|
||||||
|
|
||||||
threading.Timer(15*60,self.refresh).start() #periodic refresh: set time
|
self.glb_data = lcl_data
|
||||||
return
|
self.glb_last_refresh = time.time()
|
||||||
|
|
||||||
def get_data(self,continent_filter):
|
|
||||||
super().get_data()
|
|
||||||
self.glb_response.update({"band activity": self.glb_data[continent_filter]})
|
|
||||||
return self.glb_response
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------
|
threading.Timer(15 * 60, self.refresh).start() # periodic refresh: set time
|
||||||
|
return
|
||||||
|
|
||||||
|
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 for managing data for Spots per months chart
|
||||||
#-----------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------
|
||||||
class SpotsPerMounthProvider(BaseDataProvider):
|
class SpotsPerMounthProvider(BaseDataProvider):
|
||||||
|
def __init__(self, logger, qm):
|
||||||
|
# Calling constructor of base class
|
||||||
|
super().__init__(logger, qm, [], [])
|
||||||
|
|
||||||
def __init__(self,logger,qm):
|
def __load_data(self):
|
||||||
# Calling constructor of base class
|
|
||||||
super().__init__(logger,qm,[],[])
|
|
||||||
|
|
||||||
def __load_data(self):
|
self.logger.info("Start")
|
||||||
|
self.logger.info("doing query...")
|
||||||
|
|
||||||
self.logger.info("Start")
|
# construct final query string
|
||||||
self.logger.info("doing query...")
|
qry_string = """
|
||||||
|
|
||||||
#construct final query string
|
|
||||||
qry_string="""
|
|
||||||
select month(s1.ym) as referring_month,
|
select month(s1.ym) as referring_month,
|
||||||
cast(sum(
|
cast(sum(
|
||||||
case
|
case
|
||||||
@ -226,55 +250,59 @@ class SpotsPerMounthProvider(BaseDataProvider):
|
|||||||
group by referring_month
|
group by referring_month
|
||||||
;
|
;
|
||||||
"""
|
"""
|
||||||
self.logger.debug(qry_string)
|
self.logger.debug(qry_string)
|
||||||
self.qm.qry(qry_string)
|
self.qm.qry(qry_string)
|
||||||
data=self.qm.get_data()
|
data = self.qm.get_data()
|
||||||
if len(data)==0:
|
if len(data) == 0:
|
||||||
self.logger.warning("no data found")
|
self.logger.warning("no data found")
|
||||||
|
|
||||||
self.logger.info("query done")
|
self.logger.info("query done")
|
||||||
self.logger.debug (data)
|
self.logger.debug(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
super().refresh()
|
super().refresh()
|
||||||
lcl_data={}
|
lcl_data = {}
|
||||||
qry_data=self.__load_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})
|
|
||||||
|
|
||||||
self.logger.debug(lcl_data)
|
for i, item in enumerate(qry_data):
|
||||||
|
year_data = {"year_0": item[1], "year_1": item[2], "year_2": item[3]}
|
||||||
self.glb_data=lcl_data
|
lcl_data.update({item[0]: year_data})
|
||||||
self.glb_last_refresh=time.time()
|
|
||||||
|
|
||||||
threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time
|
self.logger.debug(lcl_data)
|
||||||
return
|
|
||||||
|
|
||||||
def get_data(self,):
|
self.glb_data = lcl_data
|
||||||
super().get_data()
|
self.glb_last_refresh = time.time()
|
||||||
self.glb_response.update({"spots_per_month": self.glb_data})
|
|
||||||
return self.glb_response
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------
|
threading.Timer(
|
||||||
|
60 * 60 * 24, self.refresh
|
||||||
|
).start() # periodic refresh: set time
|
||||||
|
return
|
||||||
|
|
||||||
|
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 for managing data for Spots trend chart
|
||||||
#-----------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------
|
||||||
class SpotsTrend(BaseDataProvider):
|
class SpotsTrend(BaseDataProvider):
|
||||||
|
def __init__(self, logger, qm):
|
||||||
|
# Calling constructor of base class
|
||||||
|
super().__init__(logger, qm, [], [])
|
||||||
|
|
||||||
def __init__(self,logger,qm):
|
def __load_data(self):
|
||||||
# Calling constructor of base class
|
|
||||||
super().__init__(logger,qm,[],[])
|
|
||||||
|
|
||||||
def __load_data(self):
|
self.logger.info("Start")
|
||||||
|
self.logger.info("doing query...")
|
||||||
|
|
||||||
self.logger.info("Start")
|
# construct final query string
|
||||||
self.logger.info("doing query...")
|
qry_string = """
|
||||||
|
|
||||||
#construct final query string
|
|
||||||
qry_string="""
|
|
||||||
select
|
select
|
||||||
FROM_UNIXTIME(time,'%Y-%m-%d') as day,
|
FROM_UNIXTIME(time,'%Y-%m-%d') as day,
|
||||||
count(0) as total
|
count(0) as total
|
||||||
@ -283,82 +311,103 @@ class SpotsTrend(BaseDataProvider):
|
|||||||
GROUP by 1
|
GROUP by 1
|
||||||
;
|
;
|
||||||
"""
|
"""
|
||||||
self.logger.debug(qry_string)
|
self.logger.debug(qry_string)
|
||||||
self.qm.qry_pd(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.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")
|
self.logger.warning("no data found")
|
||||||
|
|
||||||
#normalize data eliminating peaks
|
# normalize data eliminating peaks
|
||||||
df['day']=pd.to_datetime(df['day'])
|
df["day"] = pd.to_datetime(df["day"])
|
||||||
df=df.set_index('day')
|
df = df.set_index("day")
|
||||||
df=df.resample('D').interpolate(method='pad', limit_direction='forward', axis=0)
|
df = df.resample("D").interpolate(
|
||||||
df=df.rolling('30D').mean()
|
method="pad", limit_direction="forward", axis=0
|
||||||
df['total']=df['total'].round(0)
|
)
|
||||||
|
df = df.rolling("30D").mean()
|
||||||
|
df["total"] = df["total"].round(0)
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
super().refresh()
|
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():
|
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.logger.debug(lcl_data)
|
||||||
|
|
||||||
self.glb_data=lcl_data
|
|
||||||
self.glb_last_refresh=time.time()
|
|
||||||
|
|
||||||
threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time
|
self.glb_data = lcl_data
|
||||||
return
|
self.glb_last_refresh = time.time()
|
||||||
|
|
||||||
def get_data(self):
|
threading.Timer(
|
||||||
super().get_data()
|
60 * 60 * 24, self.refresh
|
||||||
self.glb_response.update({"spots_trend": self.glb_data})
|
).start() # periodic refresh: set time
|
||||||
return self.glb_response
|
return
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------
|
def get_data(self):
|
||||||
|
super().get_data()
|
||||||
|
self.glb_response.update({"spots_trend": self.glb_data})
|
||||||
|
return self.glb_response
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------
|
||||||
# Class for managing data for Hour/Band chart
|
# Class for managing data for Hour/Band chart
|
||||||
#-----------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------
|
||||||
class HourBand(BaseDataProvider):
|
class HourBand(BaseDataProvider):
|
||||||
|
def __init__(self, logger, qm, bands):
|
||||||
|
# Calling constructor of base class
|
||||||
|
super().__init__(logger, qm, [], bands)
|
||||||
|
|
||||||
def __init__(self,logger,qm,bands):
|
def __load_data(self):
|
||||||
# Calling constructor of base class
|
|
||||||
super().__init__(logger,qm,[],bands)
|
|
||||||
|
|
||||||
def __load_data(self):
|
self.logger.info("Start")
|
||||||
|
self.logger.info("doing query...")
|
||||||
|
|
||||||
self.logger.info("Start")
|
self.logger.debug(self.bands)
|
||||||
self.logger.info("doing query...")
|
# 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"] + '"'
|
||||||
|
|
||||||
self.logger.debug(self.bands)
|
# construct bands query weight
|
||||||
#construct bands query
|
bands_weight_qry_string = "CASE "
|
||||||
bands_qry_string = 'CASE '
|
for i in range(len(self.bands["bands"])):
|
||||||
for i in range(len(self.bands["bands"])):
|
bands_weight_qry_string += (
|
||||||
bands_qry_string+=' WHEN freq between '+str(self.bands["bands"][i]["min"])+' AND '+ str(self.bands["bands"][i]["max"])
|
" WHEN freq between "
|
||||||
bands_qry_string+=' THEN "'+self.bands["bands"][i]["id"]+'"'
|
+ 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 bands query weight
|
# construct final query string
|
||||||
bands_weight_qry_string = 'CASE '
|
qry_string = (
|
||||||
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"])+'"'
|
|
||||||
|
|
||||||
#construct final query string
|
|
||||||
qry_string ="""
|
|
||||||
select s1.band, s1.hour, s1.total from (
|
select s1.band, s1.hour, s1.total from (
|
||||||
SELECT
|
SELECT
|
||||||
cast(concat(HOUR (FROM_UNIXTIME(time))) as unsigned) as hour,
|
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
|
count(0) AS total
|
||||||
from spot
|
from spot
|
||||||
WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL 1 MONTH)
|
WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL 1 MONTH)
|
||||||
@ -368,60 +417,63 @@ class HourBand(BaseDataProvider):
|
|||||||
order by s1.band, s1.hour
|
order by s1.band, s1.hour
|
||||||
;
|
;
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
self.logger.debug(qry_string)
|
self.logger.debug(qry_string)
|
||||||
self.qm.qry(qry_string)
|
self.qm.qry(qry_string)
|
||||||
data=self.qm.get_data()
|
data = self.qm.get_data()
|
||||||
if len(data)==0:
|
if len(data) == 0:
|
||||||
self.logger.warning("no data found")
|
self.logger.warning("no data found")
|
||||||
|
|
||||||
self.logger.info("query done")
|
self.logger.info("query done")
|
||||||
self.logger.debug (data)
|
self.logger.debug(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
super().refresh()
|
super().refresh()
|
||||||
lcl_data={}
|
lcl_data = {}
|
||||||
qry_data=self.__load_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:
|
if i not in lcl_data:
|
||||||
lcl_data[i]={}
|
lcl_data[i] = {}
|
||||||
lcl_data[i].update({j:k})
|
lcl_data[i].update({j: k})
|
||||||
|
|
||||||
self.logger.debug(lcl_data)
|
self.logger.debug(lcl_data)
|
||||||
|
|
||||||
self.glb_data=lcl_data
|
|
||||||
self.glb_last_refresh=time.time()
|
|
||||||
|
|
||||||
threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time
|
self.glb_data = lcl_data
|
||||||
return
|
self.glb_last_refresh = time.time()
|
||||||
|
|
||||||
def get_data(self):
|
threading.Timer(
|
||||||
super().get_data()
|
60 * 60 * 24, self.refresh
|
||||||
self.glb_response.update({"hour_band": self.glb_data})
|
).start() # periodic refresh: set time
|
||||||
return self.glb_response
|
return
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
super().get_data()
|
||||||
|
self.glb_response.update({"hour_band": self.glb_data})
|
||||||
|
return self.glb_response
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------
|
||||||
# Class for managing data for World DX SPOTS current activity
|
# Class for managing data for World DX SPOTS current activity
|
||||||
#-----------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------
|
||||||
class WorldDxSpotsLive(BaseDataProvider):
|
class WorldDxSpotsLive(BaseDataProvider):
|
||||||
|
|
||||||
global glb_pfxt
|
global glb_pfxt
|
||||||
|
|
||||||
def __init__(self,logger,qm,pfxt):
|
def __init__(self, logger, qm, pfxt):
|
||||||
# Calling constructor of base class
|
# Calling constructor of base class
|
||||||
self.glb_pfxt=pfxt
|
self.glb_pfxt = pfxt
|
||||||
super().__init__(logger,qm,[],[])
|
super().__init__(logger, qm, [], [])
|
||||||
|
|
||||||
def __load_data(self):
|
def __load_data(self):
|
||||||
self.logger.info("Start")
|
self.logger.info("Start")
|
||||||
self.logger.info("doing query...")
|
self.logger.info("doing query...")
|
||||||
|
|
||||||
#construct final query string
|
# construct final query string
|
||||||
qry_string ="""
|
qry_string = """
|
||||||
select spotcall as dx
|
select spotcall as dx
|
||||||
from spot
|
from spot
|
||||||
WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL 1 HOUR)
|
WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL 1 HOUR)
|
||||||
@ -429,70 +481,70 @@ class WorldDxSpotsLive(BaseDataProvider):
|
|||||||
group by 1;
|
group by 1;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.logger.debug(qry_string)
|
self.logger.debug(qry_string)
|
||||||
self.qm.qry(qry_string)
|
self.qm.qry(qry_string)
|
||||||
data=self.qm.get_data()
|
data = self.qm.get_data()
|
||||||
row_headers=self.qm.get_headers()
|
row_headers = self.qm.get_headers()
|
||||||
if len(data)==0:
|
if len(data) == 0:
|
||||||
self.logger.warning("no data found")
|
self.logger.warning("no data found")
|
||||||
|
|
||||||
self.logger.info("query done")
|
self.logger.info("query done")
|
||||||
self.logger.debug (data)
|
self.logger.debug(data)
|
||||||
|
|
||||||
#define country table for search info on callsigns
|
# define country table for search info on callsigns
|
||||||
df = pd.DataFrame(columns=['row_id','dx','lat','lon'])
|
df = pd.DataFrame(columns=["row_id", "dx", "lat", "lon"])
|
||||||
dx=[]
|
dx = []
|
||||||
lat=[]
|
lat = []
|
||||||
lon=[]
|
lon = []
|
||||||
row_id=[]
|
row_id = []
|
||||||
idx=0
|
idx = 0
|
||||||
|
|
||||||
for result in data:
|
for result in data:
|
||||||
main_result=dict(zip(row_headers,result))
|
main_result = dict(zip(row_headers, result))
|
||||||
# find the country in prefix table
|
# find the country in prefix table
|
||||||
search_prefix=self.glb_pfxt.find(main_result["dx"])
|
search_prefix = self.glb_pfxt.find(main_result["dx"])
|
||||||
if search_prefix["country"] != "unknown country" :
|
if search_prefix["country"] != "unknown country":
|
||||||
# merge recordset and contry prefix
|
# merge recordset and contry prefix
|
||||||
dx.append(main_result["dx"])
|
dx.append(main_result["dx"])
|
||||||
lon.append(float(search_prefix["lat"]))
|
lon.append(float(search_prefix["lat"]))
|
||||||
lat.append(-float(search_prefix["lon"]))
|
lat.append(-float(search_prefix["lon"]))
|
||||||
idx+=1
|
idx += 1
|
||||||
row_id.append(idx)
|
row_id.append(idx)
|
||||||
|
|
||||||
df['dx']=dx
|
df["dx"] = dx
|
||||||
df['lat']=lat
|
df["lat"] = lat
|
||||||
df['lon']=lon
|
df["lon"] = lon
|
||||||
df['row_id']=row_id
|
df["row_id"] = row_id
|
||||||
df_grp=df.groupby(["lat", "lon"])["row_id"].count().reset_index(name="count")
|
df_grp = df.groupby(["lat", "lon"])["row_id"].count().reset_index(name="count")
|
||||||
|
|
||||||
if df is None ==0:
|
|
||||||
logger.warning("no data found")
|
|
||||||
|
|
||||||
return df_grp
|
if df is None == 0:
|
||||||
|
logger.warning("no data found")
|
||||||
|
|
||||||
def refresh(self):
|
return df_grp
|
||||||
super().refresh()
|
|
||||||
lcl_data={}
|
|
||||||
qry_data=self.__load_data()
|
|
||||||
|
|
||||||
self.logger.debug(qry_data)
|
def refresh(self):
|
||||||
|
super().refresh()
|
||||||
|
lcl_data = {}
|
||||||
|
qry_data = self.__load_data()
|
||||||
|
|
||||||
lcl_data=[]
|
self.logger.debug(qry_data)
|
||||||
for index, row in qry_data.iterrows():
|
|
||||||
record=dict(lat=row['lat'], lon=row['lon'], count=row['count'])
|
|
||||||
lcl_data.append(record)
|
|
||||||
|
|
||||||
self.logger.debug(lcl_data)
|
lcl_data = []
|
||||||
|
for index, row in qry_data.iterrows():
|
||||||
self.glb_data=lcl_data
|
record = dict(lat=row["lat"], lon=row["lon"], count=row["count"])
|
||||||
self.glb_last_refresh=time.time()
|
lcl_data.append(record)
|
||||||
|
|
||||||
threading.Timer(5*60,self.refresh).start() #periodic refresh: set time
|
self.logger.debug(lcl_data)
|
||||||
return
|
|
||||||
|
|
||||||
def get_data(self):
|
self.glb_data = lcl_data
|
||||||
super().get_data()
|
self.glb_last_refresh = time.time()
|
||||||
|
|
||||||
self.glb_response.update({"world_dx_spots_live": self.glb_data})
|
threading.Timer(5 * 60, self.refresh).start() # periodic refresh: set time
|
||||||
|
return
|
||||||
return self.glb_response
|
|
||||||
|
def get_data(self):
|
||||||
|
super().get_data()
|
||||||
|
|
||||||
|
self.glb_response.update({"world_dx_spots_live": self.glb_data})
|
||||||
|
|
||||||
|
return self.glb_response
|
||||||
|
78
lib/qry.py
78
lib/qry.py
@ -1,55 +1,63 @@
|
|||||||
#*****************************************************************************************
|
# *****************************************************************************************
|
||||||
# module used to make query to mysql
|
# module used to make query to mysql
|
||||||
# TODO: manage polymorfism and use only one qry sign
|
# TODO: manage polymorfism and use only one qry sign
|
||||||
#*****************************************************************************************
|
# *****************************************************************************************
|
||||||
#import MySQLdb as my
|
# import MySQLdb as my
|
||||||
import mysql.connector as my
|
import mysql.connector as my
|
||||||
from mysql.connector import pooling
|
from mysql.connector import pooling
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
import pandas as pd
|
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:
|
class query_manager:
|
||||||
#connection definition
|
# connection definition
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
try:
|
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)
|
cfg = json.load(json_data_file)
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
logging.info(e1)
|
logging.info(e1)
|
||||||
logging.info('trying with other path...')
|
logging.info("trying with other path...")
|
||||||
try:
|
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)
|
cfg = json.load(json_data_file)
|
||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
logging.error(e2)
|
logging.error(e2)
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info('config file loaded')
|
logging.info("config file loaded")
|
||||||
self.__cnxpool = pooling.MySQLConnectionPool(host=cfg['mysql']['host'],
|
self.__cnxpool = pooling.MySQLConnectionPool(
|
||||||
user=cfg['mysql']['user'],
|
host=cfg["mysql"]["host"],
|
||||||
passwd=cfg['mysql']['passwd'],
|
user=cfg["mysql"]["user"],
|
||||||
db=cfg['mysql']['db'],
|
passwd=cfg["mysql"]["passwd"],
|
||||||
charset='latin1',
|
db=cfg["mysql"]["db"],
|
||||||
# charset='utf8mb4',
|
charset="latin1",
|
||||||
# collation = 'utf8mb4_general_ci',
|
# charset='utf8mb4',
|
||||||
pool_name = "spider_pool",
|
# collation = 'utf8mb4_general_ci',
|
||||||
use_pure = True,
|
pool_name="spider_pool",
|
||||||
pool_size = 3
|
use_pure=True,
|
||||||
)
|
pool_size=3,
|
||||||
logging.info('db connection pool created')
|
)
|
||||||
|
logging.info("db connection pool created")
|
||||||
|
|
||||||
#normal query
|
# normal query
|
||||||
def qry(self,qs,prepared_statement=False):
|
def qry(self, qs, prepared_statement=False):
|
||||||
try:
|
try:
|
||||||
cnx=self.__cnxpool.get_connection()
|
cnx = self.__cnxpool.get_connection()
|
||||||
cursor = cnx.cursor(prepared=prepared_statement)
|
cursor = cnx.cursor(prepared=prepared_statement)
|
||||||
cursor.execute(qs)
|
cursor.execute(qs)
|
||||||
self.__data=cursor.fetchall()
|
self.__data = cursor.fetchall()
|
||||||
self.__row_headers=[x[0] for x in cursor.description] #this will extract row headers
|
self.__row_headers = [
|
||||||
|
x[0] for x in cursor.description
|
||||||
|
] # this will extract row headers
|
||||||
cursor.close()
|
cursor.close()
|
||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
logging.error(e2)
|
logging.error(e2)
|
||||||
@ -62,11 +70,11 @@ class query_manager:
|
|||||||
def get_headers(self):
|
def get_headers(self):
|
||||||
return self.__row_headers
|
return self.__row_headers
|
||||||
|
|
||||||
#query with pandas
|
# query with pandas
|
||||||
def qry_pd(self,qs):
|
def qry_pd(self, qs):
|
||||||
try:
|
try:
|
||||||
cnx=self.__cnxpool.get_connection()
|
cnx = self.__cnxpool.get_connection()
|
||||||
self.__data = pd.read_sql(qs,con=cnx)
|
self.__data = pd.read_sql(qs, con=cnx)
|
||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
logging.error(e2)
|
logging.error(e2)
|
||||||
finally:
|
finally:
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
#
|
#
|
||||||
# little script used to build static pages
|
# little script used to build static pages
|
||||||
#
|
#
|
||||||
__author__ = 'IU1BOW - Corrado'
|
__author__ = "IU1BOW - Corrado"
|
||||||
from staticjinja import Site
|
from staticjinja import Site
|
||||||
|
|
||||||
|
|
||||||
def cookies_check():
|
def cookies_check():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
site = Site.make_site(searchpath="../static/html/templates/",outpath="../static/html/",env_globals={
|
site = Site.make_site(
|
||||||
'cookies_check':cookies_check,
|
searchpath="../static/html/dev/",
|
||||||
})
|
outpath="../static/html/rel/",
|
||||||
site.render(use_reloader=False)
|
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
|
charset-normalizer==2.1.1
|
||||||
click==8.1.3
|
click==8.1.3
|
||||||
|
dill==0.3.6
|
||||||
docopt-ng==0.8.1
|
docopt-ng==0.8.1
|
||||||
easywatch==0.0.5
|
easywatch==0.0.5
|
||||||
Flask==2.2.2
|
Flask==2.2.2
|
||||||
@ -7,14 +9,18 @@ Flask-Minify==0.41
|
|||||||
Flask-WTF==1.0.1
|
Flask-WTF==1.0.1
|
||||||
htmlmin==0.1.12
|
htmlmin==0.1.12
|
||||||
idna==3.4
|
idna==3.4
|
||||||
|
isort==5.11.4
|
||||||
itsdangerous==2.1.2
|
itsdangerous==2.1.2
|
||||||
Jinja2==3.1.2
|
Jinja2==3.1.2
|
||||||
jsmin==3.0.1
|
jsmin==3.0.1
|
||||||
|
lazy-object-proxy==1.9.0
|
||||||
lesscpy==0.15.1
|
lesscpy==0.15.1
|
||||||
MarkupSafe==2.1.1
|
MarkupSafe==2.1.1
|
||||||
|
mccabe==0.7.0
|
||||||
mysql-connector-python==8.0.31
|
mysql-connector-python==8.0.31
|
||||||
numpy==1.24.1
|
numpy==1.24.1
|
||||||
pandas==1.5.2
|
pandas==1.5.2
|
||||||
|
platformdirs==2.6.2
|
||||||
ply==3.11
|
ply==3.11
|
||||||
protobuf==4.21.12
|
protobuf==4.21.12
|
||||||
python-dateutil==2.8.2
|
python-dateutil==2.8.2
|
||||||
@ -22,8 +28,10 @@ pytz==2022.7
|
|||||||
rcssmin==1.1.1
|
rcssmin==1.1.1
|
||||||
requests==2.28.1
|
requests==2.28.1
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
|
tomlkit==0.11.6
|
||||||
urllib3==1.26.13
|
urllib3==1.26.13
|
||||||
watchdog==2.2.0
|
watchdog==2.2.0
|
||||||
Werkzeug==2.2.2
|
Werkzeug==2.2.2
|
||||||
|
wrapt==1.14.1
|
||||||
WTForms==3.0.1
|
WTForms==3.0.1
|
||||||
xxhash==3.1.0
|
xxhash==3.1.0
|
||||||
|
187
scripts/build.sh
187
scripts/build.sh
@ -8,13 +8,141 @@ path_static='../static'
|
|||||||
path_static_html=${path_static}'/html'
|
path_static_html=${path_static}'/html'
|
||||||
path_static_js=${path_static}'/js'
|
path_static_js=${path_static}'/js'
|
||||||
path_static_css=${path_static}'/css'
|
path_static_css=${path_static}'/css'
|
||||||
|
path_cfg='../cfg'
|
||||||
|
app_ini=${path_cfg}'/webapp_log_config.ini'
|
||||||
path_docs='../docs'
|
path_docs='../docs'
|
||||||
readme='../README.md'
|
readme='../README.md'
|
||||||
manifest=${path_static}'/manifest.webmanifest'
|
manifest=${path_static}'/manifest.webmanifest'
|
||||||
changelog=${path_docs}'/'CHANGELOG.md
|
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'
|
#echo 'create modes.json from dxspider bands.pl'
|
||||||
|
|
||||||
#python ../lib/get_dxcluster_modes.py /home/sysop/spider/data/bands.pl
|
#python ../lib/get_dxcluster_modes.py /home/sysop/spider/data/bands.pl
|
||||||
#if [ "$?" != "0" ]; then
|
#if [ "$?" != "0" ]; then
|
||||||
# echo 'ERROR on creating modes.json from dxspider bands.pl'
|
# echo 'ERROR on creating modes.json from dxspider bands.pl'
|
||||||
@ -48,14 +176,14 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo 'writing version in '${changelog} '...'
|
echo 'writing version in '${changelog} '...'
|
||||||
if ! sed -i '1,4s/Release: v.*/Release: '$ver'/g' ${changelog}
|
if ! sed -i '1,4s/Release: v.*/Release: '$ver'/g' ${changelog}
|
||||||
then
|
then
|
||||||
echo 'ERROR writing version in '${changelog}
|
echo 'ERROR writing version in '${changelog}
|
||||||
exit 35
|
exit 35
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo 'writing date in '${changelog} '...'
|
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}
|
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
|
then
|
||||||
echo 'ERROR writing date in '${changelog}
|
echo 'ERROR writing date in '${changelog}
|
||||||
@ -76,58 +204,5 @@ then
|
|||||||
exit 50
|
exit 50
|
||||||
fi
|
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
|
||||||
echo Build ok
|
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
|
@ -2,11 +2,11 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
<title>DX Cluster from IU1BOW: OFFLINE</title>
|
<title>DX Cluster from IU1BOW: OFFLINE</title>
|
||||||
<!-- page generated by staticjinja -->
|
<!-- page generated by staticjinja -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="description" content="Web Ham Radio DX Cluster and spot search">
|
<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="keywords" content="ham radio, dx cluster, dx spots, cluster sposts,web dx cluster,dx cluster search, DX spots">
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<link rel="icon" href="/static/images/icons/spider_ico_master.svg" type="image/svg+xml">
|
<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="apple-touch-icon" href="/static/images/icons/icon-apple.png">
|
||||||
<link rel="manifest" href="/static/manifest.webmanifest">
|
<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' ">
|
<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>
|
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"></noscript>
|
||||||
@ -24,14 +24,14 @@
|
|||||||
<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'" >
|
<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>
|
<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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
|
||||||
|
|
||||||
<!-- nav bar -->
|
<!-- nav bar -->
|
||||||
<nav class="navbar px-2 navbar-expand-lg navbar-dark bg-dark">
|
<nav class="navbar px-2 navbar-expand-lg navbar-dark bg-dark">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
@ -47,43 +47,44 @@
|
|||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<div id="MyClockDisplay" onload="showTime()" class="text-white-50 d-none d-lg-block"></div>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
<div class="container-fluid mt-1 ml-0 mr-0 px-0">
|
<div class="container-fluid mt-1 ml-0 mr-0 px-0">
|
||||||
<div class="ipcs bg-light p-4 rounded-lg m-2">
|
<div class="ipcs bg-light p-4 rounded-lg m-2">
|
||||||
|
|
||||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||||
<p class="lead text-light">Spots list</p>
|
<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">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>
|
<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>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <div class="container">
|
<!-- <div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col align-self-center"> -->
|
<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>
|
<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
|
||||||
<p class="lead">
|
Internet</p>
|
||||||
<a class="btn btn-primary btn-lg" href="/" role="button">Try again</a>
|
<p class="lead">
|
||||||
</p>
|
<a class="btn btn-primary btn-lg" href="/" role="button">Try again</a>
|
||||||
<!-- </div>
|
</p>
|
||||||
|
<!-- </div>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<footer class="page-footer font-small blue">
|
<footer class="page-footer font-small blue">
|
||||||
<div class="footer-copyright text-center py-3">
|
<div class="footer-copyright text-center py-3">
|
||||||
@ -93,40 +94,43 @@
|
|||||||
<span id="version">v2.4.1</span>
|
<span id="version">v2.4.1</span>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script async src="static/js/clock.min.js"></script>
|
<script async src="static/js/dev/clock.js"></script>
|
||||||
<script async src="static/js/copy_date.min.js"></script>
|
<script async src="static/js/dev/copy_date.js"></script>
|
||||||
<script async src="static/js/load-sw.min.js"></script>
|
<script async src="static/js/dev/load-sw.js"></script>
|
||||||
<script nonce="sedfGFG32xs">
|
<script nonce="sedfGFG32xs">
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</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 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 -->
|
<!-- cookie consent management -->
|
||||||
|
|
||||||
|
|
||||||
<div class="modal" tabindex="-1" id="cookie-consent-container">
|
|
||||||
<div class="modal-dialog">
|
<!-- Modal for cookie consent-->
|
||||||
<div class="modal-content">
|
<div class="modal fade" id="cookie_consent_modal" tabindex="-1" aria-labelledby="cookie-consent-container" aria-hidden="true">
|
||||||
<div class="modal-header">
|
<div class="modal-dialog">
|
||||||
<h1 class="modal-title">We use cookies</h1>
|
<div class="modal-content">
|
||||||
</div>
|
<div class="modal-header">
|
||||||
<div class="modal-body">
|
<h5 class="modal-title" id="exampleModalLabel">We use cookies</h5>
|
||||||
<p>We use only technical cookies.</p>
|
</div>
|
||||||
<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 class="modal-body">
|
||||||
</div>
|
<p>We use only technical cookies.</p>
|
||||||
<div class="modal-footer">
|
<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>
|
||||||
<button type="button" class="btn btn-primary" id="cookie-consent">I agree</button>
|
</div>
|
||||||
</div>
|
<div class="modal-footer">
|
||||||
</div>
|
<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>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script defer src="static/js/dev/cookie_consent.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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
|
- single
|
||||||
semi:
|
semi:
|
||||||
- error
|
- 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,14 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
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'), {
|
let cookie_modal = new bootstrap.Modal(document.getElementById('cookie_consent_modal'), {
|
||||||
keyboard: false
|
keyboard: false
|
||||||
})
|
});
|
||||||
cookie_modal.show();
|
cookie_modal.show();
|
||||||
|
|
||||||
//if button is pressed, setting cookie
|
//if button is pressed, setting cookie
|
||||||
document.getElementById('cookie_consent_btn').onclick = function(){
|
document.getElementById('cookie_consent_btn').onclick = function(){
|
||||||
setCookie('cookie_consent',true,30);
|
setCookie('cookie_consent',true,30);
|
||||||
cookie_modal.hide();
|
cookie_modal.hide();
|
||||||
};
|
};
|
||||||
|
|
@ -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
|
* 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=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t",""));
|
||||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t",""));
|
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll('\t',''));
|
||||||
var rows_list = new Array();
|
|
||||||
|
|
||||||
refresh_timer(); //run first data fetch
|
refresh_timer(); //run first data fetch
|
||||||
var myRefresh = setInterval(refresh_timer, timer_interval_json);
|
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 */
|
/*! loadCSS. [c]2020 Filament Group, Inc. MIT License */
|
||||||
(function(w){
|
(function(w){
|
||||||
"use strict";
|
'use strict';
|
||||||
/* exported loadCSS */
|
/* exported loadCSS */
|
||||||
var loadCSS = function( href, before, media, attributes ){
|
var loadCSS = function( href, before, media, attributes ){
|
||||||
// Arguments explained:
|
// Arguments explained:
|
||||||
@ -10,13 +10,13 @@
|
|||||||
// `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all'
|
// `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.
|
// `attributes` [OPTIONAL] is the Object of attribute name/attribute value pairs to set on the stylesheet's DOM Element.
|
||||||
var doc = w.document;
|
var doc = w.document;
|
||||||
var ss = doc.createElement( "link" );
|
var ss = doc.createElement( 'link' );
|
||||||
var ref;
|
var ref;
|
||||||
if( before ){
|
if( before ){
|
||||||
ref = before;
|
ref = before;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes;
|
var refs = ( doc.body || doc.getElementsByTagName( 'head' )[ 0 ] ).childNodes;
|
||||||
ref = refs[ refs.length - 1];
|
ref = refs[ refs.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,10 +29,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ss.rel = "stylesheet";
|
ss.rel = 'stylesheet';
|
||||||
ss.href = href;
|
ss.href = href;
|
||||||
// temporarily set media to something inapplicable to ensure it'll fetch without blocking render
|
// 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.
|
// wait until body is defined before injecting link. This ensures a non-blocking load in IE11.
|
||||||
function ready( cb ){
|
function ready( cb ){
|
||||||
@ -44,8 +44,8 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Inject link
|
// Inject link
|
||||||
// Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs
|
// Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs
|
||||||
// Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/
|
// Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/
|
||||||
ready( function(){
|
ready( function(){
|
||||||
ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) );
|
ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) );
|
||||||
});
|
});
|
||||||
@ -65,24 +65,24 @@
|
|||||||
|
|
||||||
function loadCB(){
|
function loadCB(){
|
||||||
if( ss.addEventListener ){
|
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
|
// once loaded, set link's media back to `all` so that the stylesheet applies once it loads
|
||||||
if( ss.addEventListener ){
|
if( ss.addEventListener ){
|
||||||
ss.addEventListener( "load", loadCB);
|
ss.addEventListener( 'load', loadCB);
|
||||||
}
|
}
|
||||||
ss.onloadcssdefined = onloadcssdefined;
|
ss.onloadcssdefined = onloadcssdefined;
|
||||||
onloadcssdefined( loadCB );
|
onloadcssdefined( loadCB );
|
||||||
return ss;
|
return ss;
|
||||||
};
|
};
|
||||||
// commonjs
|
// commonjs
|
||||||
if( typeof exports !== "undefined" ){
|
if( typeof exports !== 'undefined' ){
|
||||||
exports.loadCSS = loadCSS;
|
exports.loadCSS = loadCSS;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
w.loadCSS = loadCSS;
|
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 +1 @@
|
|||||||
document.getElementById("copyDate").innerHTML="2020-".concat((new Date).getFullYear());
|
document.getElementById("copyDate").innerHTML="2020-".concat((new Date).getFullYear());
|
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="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="apple-touch-icon" href="/static/images/icons/icon-apple.png">
|
||||||
<link rel="manifest" href="/static/manifest.webmanifest">
|
<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' ">
|
<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>
|
<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'" >
|
<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>
|
<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 %}
|
{% endblock head %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -89,21 +89,18 @@
|
|||||||
<span id="version">v2.4.1</span>
|
<span id="version">v2.4.1</span>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script async class="spiderscript" src="static/js/clock.min.js"></script>
|
<script async src="static/js/rel/clock.min.js"></script>
|
||||||
<script async class="spiderscript" src="static/js/copy_date.min.js"></script>
|
<script async src="static/js/rel/copy_date.min.js"></script>
|
||||||
<script async class="spiderscript" src="static/js/load-sw.min.js"></script>
|
<script async src="static/js/rel/load-sw.min.js"></script>
|
||||||
<script nonce="sedfGFG32xs">
|
<script nonce="sedfGFG32xs">
|
||||||
{% block app_data %}
|
{% block app_data %}
|
||||||
var my_callsign='{{callsign}}';
|
var my_callsign='{{callsign}}';
|
||||||
{% endblock app_data %}
|
{% endblock app_data %}
|
||||||
</script>
|
</script>
|
||||||
<script defer class="spiderscript" src="static/js/common.js"></script>
|
<script defer src="static/js/rel/common.min.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="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></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 %}
|
{% 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 %}
|
{% endblock app_scripts %}
|
||||||
{% block inline_scripts %}
|
{% block inline_scripts %}
|
||||||
{% endblock inline_scripts %}
|
{% endblock inline_scripts %}
|
||||||
@ -131,7 +128,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 %}
|
{% endif %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
{% extends "index.html" %}
|
{% extends "index.html" %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% block title %}
|
{% block title %}
|
||||||
<title>Spot search for a specific Callsign</title>
|
<title>Spot search for a specific Callsign</title>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
{% block titles %}
|
{% block titles %}
|
||||||
<h1 class="display-4 text-white">{{callsign}}</h1>
|
<h1 class="display-4 text-white">{{callsign}}</h1>
|
||||||
<p class="lead text-light">Some statistics about this callsign</p>
|
<p class="lead text-light">Some statistics about this callsign</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block filters %}
|
{% block filters %}
|
||||||
<div class="row mx-auto">
|
<div class="row mx-auto">
|
||||||
{% endblock filters %}
|
{% endblock filters %}
|
||||||
{% block inline_scripts %}
|
{% 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 %}
|
{% 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,33 +1,47 @@
|
|||||||
{% extends "_base.html" %}
|
{% extends "_base.html" %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% block title %}
|
{% block title %}
|
||||||
<title>DX Cluster from IU1BOW: Cookies</title>
|
<title>DX Cluster from IU1BOW: Cookies</title>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
{% block titles %}
|
{% block titles %}
|
||||||
<h1 class="display-4 text-white">COOKIES</h1>
|
<h1 class="display-4 text-white">COOKIES</h1>
|
||||||
<p class="lead text-light">WEB DX Cluster For HAM Radio</p>
|
<p class="lead text-light">WEB DX Cluster For HAM Radio</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block filters %}
|
{% block filters %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block contents %}
|
{% block contents %}
|
||||||
<div class="col mr-3 px-2">
|
<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
|
<p class="text-justify"><span class="font-weight-bold">Cookies</span> are small text files that can be used by
|
||||||
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
|
websites to make a user's experience more efficient. This site uses different types of cookies. You can at any time
|
||||||
please check the websites of these third parties for more information about their cookies and how they manage them.</p>
|
change or withdraw
|
||||||
<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
|
your consent from the Cookies page on my website. Some cookies are placed by third party services that appear on our
|
||||||
these cookies.</p>
|
pages, for example if you view or listen to any embedded audio or video content. I don't control the setting of
|
||||||
<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>
|
these cookies, so
|
||||||
<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>
|
please check the websites of these third parties for more information about their cookies and how they manage them.
|
||||||
<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>
|
||||||
valuable for publishers and third party advertisers.</p>
|
<p class="text-justify"><span class="font-weight-bold">Necessary</span> cookies help make a website usable by enabling
|
||||||
<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>
|
basic functions like page navigation and access to secure areas of the website. The website cannot function properly
|
||||||
</div>
|
without
|
||||||
{% endblock %}
|
these cookies.</p>
|
||||||
{% block app_data %}
|
<p class="text-justify"><span class="font-weight-bold">Preference</span> cookies enable a website to remember
|
||||||
{% endblock %}
|
information that changes the way the website behaves or looks, like your preferred language or the region that you
|
||||||
{% block app_scritps %}
|
are in.</p>
|
||||||
{% endblock %}
|
<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 %}
|
@ -1,210 +1,211 @@
|
|||||||
{% extends "_base.html" %}
|
{% extends "_base.html" %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% block title %}
|
{% block title %}
|
||||||
<title>DX Cluster / DX Spot for Hamradio</title>
|
<title>DX Cluster / DX Spot for Hamradio</title>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
{% block titles %}
|
{% block titles %}
|
||||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||||
<p class="lead text-light">Spots list</p>
|
<p class="lead text-light">Spots list</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block filters %}
|
{% block filters %}
|
||||||
<div class="row mx-auto justify-content-between align-middle">
|
<div class="row mx-auto justify-content-between align-middle">
|
||||||
<div class="mx-auto">
|
<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"
|
||||||
<span class="bi-funnel-fill" role="button" aria-label="funnel-fill"></span>
|
aria-label="filter" data-bs-toggle="collapse" data-bs-target="#collapseFilters">
|
||||||
</button>
|
<span class="bi-funnel-fill" role="button" aria-label="funnel-fill"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</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() }}" />
|
||||||
|
<strong>Band</strong>
|
||||||
|
<select class="form-select overflow-hidden" id="band" size="14" multiple>
|
||||||
|
{% for dict_item in bands['bands']|reverse %}
|
||||||
|
<option selected value="{{dict_item['id']}}">{{dict_item["id"]}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<p></p>
|
||||||
|
<div class="row">
|
||||||
|
<strong>Mode</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<select class="form-select overflow-hidden" id="mode" size="3" multiple>
|
||||||
<div class="row mx-auto">
|
<option selected value="cw">CW</option>
|
||||||
<!--Sidebar content-->
|
<option selected value="phone">PHONE</option>
|
||||||
<div class="col-md-auto ml-2 collapse rounded-sm shadow mb-5 bg-body" id="collapseFilters">
|
<option selected value="digi">DIGI</option>
|
||||||
<form method="POST" id="form-filters" enctype="multipart/form-data" >
|
</select>
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
<p></p>
|
||||||
<strong>Band</strong>
|
<div class="row">
|
||||||
<select class="form-select overflow-hidden" id="band" size="14" multiple>
|
<div class="col">
|
||||||
{% for dict_item in bands['bands']|reverse %}
|
<strong>De</strong>
|
||||||
|
<select class="form-select overflow-hidden" id="de_re" size="7" multiple>
|
||||||
|
{% for dict_item in continents['continents'] %}
|
||||||
<option selected value="{{dict_item['id']}}">{{dict_item["id"]}}</option>
|
<option selected value="{{dict_item['id']}}">{{dict_item["id"]}}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</select>
|
</select>
|
||||||
<p></p>
|
</div>
|
||||||
<div class="row">
|
<div class="col">
|
||||||
<strong>Mode</strong>
|
<strong>Dx</strong>
|
||||||
</div>
|
<select class="form-select overflow-hidden" id="dx_re" size="7" multiple>
|
||||||
<select class="form-select overflow-hidden" id="mode" size="3" multiple>
|
{% for dict_item in continents['continents'] %}
|
||||||
<option selected value="cw">CW</option>
|
<option selected value="{{dict_item['id']}}">{{dict_item["id"]}}</option>
|
||||||
<option selected value="phone">PHONE</option>
|
{% endfor %}
|
||||||
<option selected value="digi">DIGI</option>
|
|
||||||
</select>
|
</select>
|
||||||
<p></p>
|
</div>
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<strong>De</strong>
|
|
||||||
<select class="form-select overflow-hidden" id="de_re" size="7" multiple>
|
|
||||||
{% for dict_item in continents['continents'] %}
|
|
||||||
<option selected value="{{dict_item['id']}}">{{dict_item["id"]}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<strong>Dx</strong>
|
|
||||||
<select class="form-select overflow-hidden" id="dx_re" size="7" multiple>
|
|
||||||
{% for dict_item in continents['continents'] %}
|
|
||||||
<option selected value="{{dict_item['id']}}">{{dict_item["id"]}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p></p>
|
|
||||||
<!-- cq filter -->
|
|
||||||
{% if enable_cq_filter == "Y" %}
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p></p>
|
|
||||||
<div class="row">
|
|
||||||
<strong>CQ Dx zone</strong>
|
|
||||||
<div class="col">
|
|
||||||
<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>
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p></p>
|
||||||
|
<!-- cq filter -->
|
||||||
|
{% if enable_cq_filter == "Y" %}
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p></p>
|
||||||
|
<div class="row">
|
||||||
|
<strong>CQ Dx zone</strong>
|
||||||
|
<div class="col">
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock filters %}
|
{% endblock filters %}
|
||||||
<!-- table -->
|
<!-- table -->
|
||||||
{% block contents %}
|
{% block contents %}
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<table id="spotsTable" class="table table-striped table-sm text-responsive table-borderless table-hover">
|
<table id="spotsTable" class="table table-striped table-sm text-responsive table-borderless table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">DE</th>
|
<th scope="col">DE</th>
|
||||||
<th scope="col">Freq</th>
|
<th scope="col">Freq</th>
|
||||||
<th scope="col">DX</th>
|
<th scope="col">DX</th>
|
||||||
<th scope="col"></th>
|
<th scope="col"></th>
|
||||||
<th scope="col" class="d-none d-lg-table-cell d-xl-table-cell">Country</th>
|
<th scope="col" class="d-none d-lg-table-cell d-xl-table-cell">Country</th>
|
||||||
<th scope="col" class="d-none d-lg-table-cell d-xl-table-cell">Comments</th>
|
<th scope="col" class="d-none d-lg-table-cell d-xl-table-cell">Comments</th>
|
||||||
<th scope="col">UTC</th>
|
<th scope="col">UTC</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="bodyspot">
|
<tbody id="bodyspot">
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock contents %}
|
{% endblock contents %}
|
||||||
{% block app_data %}
|
{% block app_data %}
|
||||||
{{ super() }}
|
{{ 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 %}
|
|
||||||
|
|
||||||
|
|
||||||
|
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,40 +1,42 @@
|
|||||||
{% extends "_base.html" %}
|
{% extends "_base.html" %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% block title %}
|
{% block title %}
|
||||||
<title>DX Cluster from IU1BOW: OFFLINE</title>
|
<title>DX Cluster from IU1BOW: OFFLINE</title>
|
||||||
<!-- page generated by staticjinja -->
|
<!-- page generated by staticjinja -->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
{% block menu %}
|
{% block menu %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% block callsign %}
|
{% block callsign %}
|
||||||
{% endblock callsign %}
|
{% endblock callsign %}
|
||||||
{% endblock menu %}
|
{% endblock menu %}
|
||||||
{% block titles %}
|
{% block titles %}
|
||||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||||
<p class="lead text-light">Spots list</p>
|
<p class="lead text-light">Spots list</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block filters %}
|
{% block filters %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block contents %}
|
{% block contents %}
|
||||||
<!-- <div class="container">
|
<!-- <div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col align-self-center"> -->
|
<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>
|
<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
|
||||||
<p class="lead">
|
Internet</p>
|
||||||
<a class="btn btn-primary btn-lg" href="/" role="button">Try again</a>
|
<p class="lead">
|
||||||
</p>
|
<a class="btn btn-primary btn-lg" href="/" role="button">Try again</a>
|
||||||
<!-- </div>
|
</p>
|
||||||
|
<!-- </div>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block app_data %}
|
{% block app_data %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block app_scritps %}
|
{% block app_scritps %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,100 +1,101 @@
|
|||||||
{% extends "_base.html" %}
|
{% extends "_base.html" %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% block title %}
|
{% block title %}
|
||||||
<title>Some charts end stats from the dx clustes node</title>
|
<title>Some charts end stats from the dx clustes node</title>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<meta http-equiv="refresh" content="300">
|
<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 %}
|
{% endblock %}
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
{% block titles %}
|
{% block titles %}
|
||||||
<h1 class="display-4 text-white">PLOTS & STATS</h1>
|
<h1 class="display-4 text-white">PLOTS & STATS</h1>
|
||||||
<p class="lead text-light">Some statistics about this node</p>
|
<p class="lead text-light">Some statistics about this node</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block filters %}
|
{% block filters %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block contents %}
|
{% 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="shadow-lg bg-body mb-5 rounded" id="form-band_activity">
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<form method="POST" id="form-continents" enctype="multipart/form-data" >
|
<form method="POST" id="form-continents" enctype="multipart/form-data">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||||
<div class="container">
|
<div class="container">
|
||||||
Your continent is:
|
Your continent is:
|
||||||
<select class="form-select flex-shrink" aria-label="continent" id="continentInput">
|
<select class="form-select flex-shrink" aria-label="continent" id="continentInput">
|
||||||
{% for dict_item in continents['continents']%}
|
{% 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 %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div id="chart-band_activity"></div>
|
<div id="chart-band_activity"></div>
|
||||||
</div>
|
|
||||||
<small><sup id="txt_continent"></sup></small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="shadow-lg mb-5 bg-body rounded" id="chart-world_dx_spots_live"></div>
|
|
||||||
|
|
||||||
<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">
|
|
||||||
</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>
|
|
||||||
<hr>
|
|
||||||
<table class="table table-striped table-borderless table-sm text-responsive table-hover">
|
|
||||||
<thead id="telnet-thead">
|
|
||||||
<tr>
|
|
||||||
<th scope="col">Callsign</th>
|
|
||||||
<th scope="col">Type</th>
|
|
||||||
<th scope="col">Started</th>
|
|
||||||
<th scope="col" class="d-none d-lg-table-cell d-xl-table-cell">Name</th>
|
|
||||||
<th scope="col">Avg RTT</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for dict_item in who %}
|
|
||||||
<tr>
|
|
||||||
<td>{{dict_item["callsign"]}}</td>
|
|
||||||
<td>{{dict_item["type"]}}</td>
|
|
||||||
<td>{{dict_item["started"]}}</td>
|
|
||||||
<td class="d-none d-lg-table-cell d-xl-table-cell">{{dict_item["name"]}}</td>
|
|
||||||
<td>{{dict_item["average_rtt"]}}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
<small><sup id="txt_continent"></sup></small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="shadow-lg mb-5 bg-body rounded" id="chart-world_dx_spots_live"></div>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
</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>
|
||||||
|
<hr>
|
||||||
|
<table class="table table-striped table-borderless table-sm text-responsive table-hover">
|
||||||
|
<thead id="telnet-thead">
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Callsign</th>
|
||||||
|
<th scope="col">Type</th>
|
||||||
|
<th scope="col">Started</th>
|
||||||
|
<th scope="col" class="d-none d-lg-table-cell d-xl-table-cell">Name</th>
|
||||||
|
<th scope="col">Avg RTT</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for dict_item in who %}
|
||||||
|
<tr>
|
||||||
|
<td>{{dict_item["callsign"]}}</td>
|
||||||
|
<td>{{dict_item["type"]}}</td>
|
||||||
|
<td>{{dict_item["started"]}}</td>
|
||||||
|
<td class="d-none d-lg-table-cell d-xl-table-cell">{{dict_item["name"]}}</td>
|
||||||
|
<td>{{dict_item["average_rtt"]}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock contents %}
|
</div>
|
||||||
|
{% endblock contents %}
|
||||||
{% block app_data %}
|
|
||||||
{{ super() }}
|
{% block app_data %}
|
||||||
var continents_cq={{continents["continents"]|tojson|safe}};
|
{{ super() }}
|
||||||
var band_frequencies={{bands["bands"]|tojson|safe}};
|
var continents_cq={{continents["continents"]|tojson|safe}};
|
||||||
{% endblock app_data %}
|
var band_frequencies={{bands["bands"]|tojson|safe}};
|
||||||
|
{% endblock app_data %}
|
||||||
{% block app_scripts %}
|
|
||||||
{{ super() }}
|
{% block app_scripts %}
|
||||||
<script defer class="spiderscript" src="static/js/plot_band_activity.js"></script>
|
{{ super() }}
|
||||||
<script defer class="spiderscript" src="static/js/plot_world_dx_spots_live.js"></script>
|
<script defer src="static/js/rel/plot_band_activity.min.js"></script>
|
||||||
<script defer class="spiderscript" src="static/js/plot_hour_band.js"></script>
|
<script defer src="static/js/rel/plot_world_dx_spots_live.min.js"></script>
|
||||||
<script defer class="spiderscript" src="static/js/plot_dx_spots_trend.js"></script>
|
<script defer src="static/js/rel/plot_hour_band.min.js"></script>
|
||||||
<script defer class="spiderscript" src="static/js/plot_dx_spots_per_month.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,75 +1,120 @@
|
|||||||
{% extends "_base.html" %}
|
{% extends "_base.html" %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% block title %}
|
{% block title %}
|
||||||
<title>DX Cluster from IU1BOW: Privacy</title>
|
<title>DX Cluster from IU1BOW: Privacy</title>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
{% block titles %}
|
{% block titles %}
|
||||||
<h1 class="display-4 text-white">PRIVACY</h1>
|
<h1 class="display-4 text-white">PRIVACY</h1>
|
||||||
<p class="lead text-light">WEB DX Cluster For HAM Radio</p>
|
<p class="lead text-light">WEB DX Cluster For HAM Radio</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block filters %}
|
{% block filters %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block contents %}
|
{% block contents %}
|
||||||
<div class="col mr-3 px-2">
|
<div class="col mr-3 px-2">
|
||||||
<h1>Privacy Policy for this web site</h1>
|
<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>At this web site, one of our main priorities is the privacy of our visitors. This Privacy Policy document
|
||||||
<p>If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact us.</p>
|
contains types of information that is collected and recorded by this web site and how we use it.</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>
|
<p>If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact
|
||||||
<h2>Consent</h2>
|
us.</p>
|
||||||
<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>
|
<p>This Privacy Policy applies only to our online activities and is valid for visitors to our website with regards
|
||||||
<h2>Information we collect</h2>
|
to the information that they shared and/or collect in this web site. This policy is not applicable to any
|
||||||
<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>
|
information collected offline or via channels other than this website. Our Privacy Policy was created with the
|
||||||
<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>
|
help of the <a href="https://www.privacypolicygenerator.info">Privacy Policy Generator</a> and the <a
|
||||||
<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>
|
href="https://www.generateprivacypolicy.com/">Free Privacy Policy Generator</a>.</p>
|
||||||
<h2>How we use your information</h2>
|
<h2>Consent</h2>
|
||||||
<p>We use the information we collect in various ways, including to:</p>
|
<p>By using our website, you hereby consent to our Privacy Policy and agree to its terms. For our Terms and
|
||||||
<ul>
|
Conditions, please visit the <a href="https://www.privacypolicyonline.com/terms-conditions-generator/">Terms &
|
||||||
<li>Provide, operate, and maintain our webste</li>
|
Conditions Generator</a>.</p>
|
||||||
<li>Improve, personalize, and expand our webste</li>
|
<h2>Information we collect</h2>
|
||||||
<li>Understand and analyze how you use our webste</li>
|
<p>The personal information that you are asked to provide, and the reasons why you are asked to provide it, will be
|
||||||
<li>Develop new products, services, features, and functionality</li>
|
made clear to you at the point we ask you to provide your personal information.</p>
|
||||||
<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>
|
<p>If you contact us directly, we may receive additional information about you such as your name, email address,
|
||||||
<li>Send you emails</li>
|
phone number, the contents of the message and/or attachments you may send us, and any other information you may
|
||||||
<li>Find and prevent fraud</li>
|
choose to provide.</p>
|
||||||
</ul>
|
<p>When you register for an Account, we may ask for your contact information, including items such as name, company
|
||||||
<h2>Log Files</h2>
|
name, address, email address, and telephone number.</p>
|
||||||
<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>How we use your information</h2>
|
||||||
<h2>Cookies and Web Beacons</h2>
|
<p>We use the information we collect in various ways, including to:</p>
|
||||||
<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>
|
<ul>
|
||||||
<p>For more general information on cookies, please read <a href="https://www.privacypolicies.com/blog/cookies/">"What Are Cookies"</a>.</p>
|
<li>Provide, operate, and maintain our webste</li>
|
||||||
<h2>Advertising Partners Privacy Policies</h2>
|
<li>Improve, personalize, and expand our webste</li>
|
||||||
<P>You may consult this list to find the Privacy Policy for each of the advertising partners of this web site.</p>
|
<li>Understand and analyze how you use our webste</li>
|
||||||
<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>
|
<li>Develop new products, services, features, and functionality</li>
|
||||||
<p>Note that this web site has no access to or control over these cookies that are used by third-party advertisers.</p>
|
<li>Communicate with you, either directly or through one of our partners, including for customer service, to
|
||||||
<h2>Third Party Privacy Policies</h2>
|
provide you with updates and other information relating to the webste, and for marketing and promotional
|
||||||
<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>
|
purposes</li>
|
||||||
<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>
|
<li>Send you emails</li>
|
||||||
<h2>CCPA Privacy Rights (Do Not Sell My Personal Information)</h2>
|
<li>Find and prevent fraud</li>
|
||||||
<p>Under the CCPA, among other rights, California consumers have the right to:</p>
|
</ul>
|
||||||
<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>
|
<h2>Log Files</h2>
|
||||||
<p>Request that a business delete any personal data about the consumer that a business has collected.</p>
|
<p>this web site follows a standard procedure of using log files. These files log visitors when they visit websites.
|
||||||
<p>Request that a business that sells a consumer's personal data, not sell the consumer's personal data.</p>
|
All hosting companies do this and a part of hosting services' analytics. The information collected by log files
|
||||||
<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>
|
include internet protocol (IP) addresses, browser type, Internet Service Provider (ISP), date and time stamp,
|
||||||
<h2>GDPR Data Protection Rights</h2>
|
referring/exit pages, and possibly the number of clicks. These are not linked to any information that is
|
||||||
<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>
|
personally identifiable. The purpose of the information is for analyzing trends, administering the site,
|
||||||
<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>
|
tracking users' movement on the website, and gathering demographic information.</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>
|
<h2>Cookies and Web Beacons</h2>
|
||||||
<p>The right to erasure – You have the right to request that we erase your personal data, under certain conditions.</p>
|
<p>Like any other website, this web site uses 'cookies'. These cookies are used to store information including
|
||||||
<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>
|
visitors' preferences, and the pages on the website that the visitor accessed or visited. The information is
|
||||||
<p>The right to object to processing – You have the right to object to our processing of your personal data, under certain conditions.</p>
|
used to optimize the users' experience by customizing our web page content based on visitors' browser type
|
||||||
<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>
|
and/or other information.</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>
|
<p>For more general information on cookies, please read <a
|
||||||
<h2>Children's Information</h2>
|
href="https://www.privacypolicies.com/blog/cookies/">"What Are Cookies"</a>.</p>
|
||||||
<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>
|
<h2>Advertising Partners Privacy Policies</h2>
|
||||||
<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>
|
<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>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block app_data %}
|
{% block app_data %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block app_scritps %}
|
{% block app_scritps %}
|
||||||
{% endblock %}
|
{% endblock %}
|
21
test.sh
21
test.sh
@ -1,8 +1,21 @@
|
|||||||
if [ "$1" == "-b" ]; then
|
if [ $# -gt 0 ]
|
||||||
|
then
|
||||||
cd scripts || exit
|
cd scripts || exit
|
||||||
./build.sh
|
if ! ./build.sh ${1}
|
||||||
|
then
|
||||||
|
cd ..
|
||||||
|
echo "terminated"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
cd ..
|
cd ..
|
||||||
fi
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
496
webapp.py
496
webapp.py
@ -1,4 +1,4 @@
|
|||||||
__author__ = 'IU1BOW - Corrado'
|
__author__ = "IU1BOW - Corrado"
|
||||||
import os
|
import os
|
||||||
import flask
|
import flask
|
||||||
from flask import request, render_template, jsonify
|
from flask import request, render_template, jsonify
|
||||||
@ -23,141 +23,163 @@ logger = logging.getLogger(__name__)
|
|||||||
logger.info("Start")
|
logger.info("Start")
|
||||||
|
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
app.config["DEBUG"] = False
|
app.config["SECRET_KEY"] = "secret!"
|
||||||
app.config['SECRET_KEY'] = 'secret!'
|
|
||||||
app.config.update(
|
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_HTTPONLY=True,
|
||||||
SESSION_COOKIE_SAMESITE='Strict',
|
SESSION_COOKIE_SAMESITE="Strict",
|
||||||
)
|
)
|
||||||
|
|
||||||
csrf = CSRFProtect(app)
|
csrf = CSRFProtect(app)
|
||||||
#minify(app=app, html=True, js=True,cssless=False)
|
logger.debug(app.config)
|
||||||
minify(app=app, html=False, js=False,cssless=False)
|
|
||||||
|
|
||||||
#load config file
|
if app.config["DEBUG"]:
|
||||||
with open('cfg/config.json') as json_data_file:
|
minify(app=app, html=False, js=False, cssless=False)
|
||||||
cfg = json.load(json_data_file)
|
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:")
|
||||||
logging.debug(cfg)
|
logging.debug(cfg)
|
||||||
#load bands file
|
# load bands file
|
||||||
with open('cfg/bands.json') as json_bands:
|
with open("cfg/bands.json") as json_bands:
|
||||||
band_frequencies = json.load(json_bands)
|
band_frequencies = json.load(json_bands)
|
||||||
|
|
||||||
#load mode file
|
# load mode file
|
||||||
with open('cfg/modes.json') as json_modes:
|
with open("cfg/modes.json") as json_modes:
|
||||||
modes_frequencies = json.load(json_modes)
|
modes_frequencies = json.load(json_modes)
|
||||||
|
|
||||||
#load continents-cq file
|
# load continents-cq file
|
||||||
with open('cfg/continents.json') as json_continents:
|
with open("cfg/continents.json") as json_continents:
|
||||||
continents_cq = json.load(json_continents)
|
continents_cq = json.load(json_continents)
|
||||||
|
|
||||||
#read and set default for enabling cq filter
|
# read and set default for enabling cq filter
|
||||||
if cfg.get('enable_cq_filter'):
|
if cfg.get("enable_cq_filter"):
|
||||||
enable_cq_filter=cfg['enable_cq_filter'].upper()
|
enable_cq_filter = cfg["enable_cq_filter"].upper()
|
||||||
else:
|
else:
|
||||||
enable_cq_filter='N'
|
enable_cq_filter = "N"
|
||||||
|
|
||||||
#define country table for search info on callsigns
|
# define country table for search info on callsigns
|
||||||
pfxt=prefix_table()
|
pfxt = prefix_table()
|
||||||
|
|
||||||
#create object query manager
|
# create object query manager
|
||||||
qm=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):
|
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):
|
def query_build_callsign(callsign):
|
||||||
|
|
||||||
query_string=''
|
query_string = ""
|
||||||
if len(callsign)<=14:
|
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 = (
|
||||||
query_string+=" ORDER BY rowid desc limit 10)"
|
"(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotter='"
|
||||||
query_string+=" UNION "
|
+ callsign
|
||||||
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 += " 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:
|
else:
|
||||||
logging.warning('callsign too long')
|
logging.warning("callsign too long")
|
||||||
return query_string
|
return query_string
|
||||||
|
|
||||||
|
|
||||||
def query_build():
|
def query_build():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
#get url parameters
|
# get url parameters
|
||||||
last_rowid=request.args.get('lr') #Last rowid fetched by front end
|
last_rowid = request.args.get("lr") # Last rowid fetched by front end
|
||||||
band=(request.args.getlist('b')) #band filter
|
band = request.args.getlist("b") # band filter
|
||||||
dere=(request.args.getlist('e')) #DE continent filter
|
dere = request.args.getlist("e") # DE continent filter
|
||||||
dxre=(request.args.getlist('x')) #Dx continent filter
|
dxre = request.args.getlist("x") # Dx continent filter
|
||||||
mode=(request.args.getlist('m')) #mode filter
|
mode = request.args.getlist("m") # mode filter
|
||||||
decq=(request.args.getlist('qe')) #DE cq zone filter
|
decq = request.args.getlist("qe") # DE cq zone filter
|
||||||
dxcq=(request.args.getlist('qx')) #DX 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):
|
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:
|
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
|
# construct mode query
|
||||||
mode_qry_string = ' AND (('
|
mode_qry_string = " AND (("
|
||||||
for i,item_mode in enumerate(mode):
|
for i, item_mode in enumerate(mode):
|
||||||
single_mode=find_id_json(modes_frequencies["modes"],item_mode)
|
single_mode = find_id_json(modes_frequencies["modes"], item_mode)
|
||||||
if i > 0:
|
if i > 0:
|
||||||
mode_qry_string +=') OR ('
|
mode_qry_string += ") OR ("
|
||||||
for j in range(len(single_mode["freq"])):
|
for j in range(len(single_mode["freq"])):
|
||||||
if j > 0:
|
if j > 0:
|
||||||
mode_qry_string +=') OR ('
|
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 += (
|
||||||
|
"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
|
# construct DE continent region query
|
||||||
dere_qry_string = ' AND spottercq IN ('
|
dere_qry_string = " AND spottercq IN ("
|
||||||
for i, item_dere in enumerate(dere):
|
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:
|
if i > 0:
|
||||||
dere_qry_string +=','
|
dere_qry_string += ","
|
||||||
dere_qry_string += str(continent["cq"])
|
dere_qry_string += str(continent["cq"])
|
||||||
dere_qry_string +=')'
|
dere_qry_string += ")"
|
||||||
|
|
||||||
#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)
|
|
||||||
if i > 0:
|
|
||||||
dxre_qry_string +=','
|
|
||||||
dxre_qry_string += str(continent["cq"])
|
|
||||||
dxre_qry_string +=')'
|
|
||||||
|
|
||||||
if enable_cq_filter == 'Y':
|
# construct DX continent region query
|
||||||
#construct de cq query
|
dxre_qry_string = " AND spotcq IN ("
|
||||||
decq_qry_string = ''
|
for i, item_dxre in enumerate(dxre):
|
||||||
if len(decq)==1:
|
continent = find_id_json(continents_cq["continents"], item_dxre)
|
||||||
|
if i > 0:
|
||||||
|
dxre_qry_string += ","
|
||||||
|
dxre_qry_string += str(continent["cq"])
|
||||||
|
dxre_qry_string += ")"
|
||||||
|
|
||||||
|
if enable_cq_filter == "Y":
|
||||||
|
# construct de cq query
|
||||||
|
decq_qry_string = ""
|
||||||
|
if len(decq) == 1:
|
||||||
if decq[0].isnumeric():
|
if decq[0].isnumeric():
|
||||||
decq_qry_string = ' AND spottercq =' + decq[0]
|
decq_qry_string = " AND spottercq =" + decq[0]
|
||||||
#construct dx cq query
|
# construct dx cq query
|
||||||
dxcq_qry_string = ''
|
dxcq_qry_string = ""
|
||||||
if len(dxcq)==1:
|
if len(dxcq) == 1:
|
||||||
if dxcq[0].isnumeric():
|
if dxcq[0].isnumeric():
|
||||||
dxcq_qry_string = ' AND spotcq =' + dxcq[0]
|
dxcq_qry_string = " AND spotcq =" + dxcq[0]
|
||||||
|
|
||||||
if last_rowid is None:
|
if last_rowid is None:
|
||||||
last_rowid = "0"
|
last_rowid = "0"
|
||||||
if not last_rowid.isnumeric():
|
if not last_rowid.isnumeric():
|
||||||
last_rowid = 0
|
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:
|
if len(band) > 0:
|
||||||
query_string += band_qry_string
|
query_string += band_qry_string
|
||||||
|
|
||||||
@ -170,205 +192,283 @@ def query_build():
|
|||||||
if len(dxre) > 0:
|
if len(dxre) > 0:
|
||||||
query_string += dxre_qry_string
|
query_string += dxre_qry_string
|
||||||
|
|
||||||
if enable_cq_filter == 'Y':
|
if enable_cq_filter == "Y":
|
||||||
if len(decq_qry_string) > 0:
|
if len(decq_qry_string) > 0:
|
||||||
query_string += decq_qry_string
|
query_string += decq_qry_string
|
||||||
|
|
||||||
if len(dxcq_qry_string) > 0:
|
if len(dxcq_qry_string) > 0:
|
||||||
query_string += dxcq_qry_string
|
query_string += dxcq_qry_string
|
||||||
|
|
||||||
query_string += " ORDER BY rowid desc limit 50;"
|
query_string += " ORDER BY rowid desc limit 50;"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
query_string = ''
|
query_string = ""
|
||||||
|
|
||||||
return 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
|
|
||||||
def spotquery():
|
|
||||||
try:
|
|
||||||
|
|
||||||
callsign=request.args.get('c') #search specific callsign
|
# 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
|
||||||
|
|
||||||
if callsign:
|
if callsign:
|
||||||
query_string=query_build_callsign(callsign)
|
query_string = query_build_callsign(callsign)
|
||||||
else:
|
else:
|
||||||
query_string=query_build()
|
query_string = query_build()
|
||||||
|
|
||||||
qm.qry(query_string)
|
qm.qry(query_string)
|
||||||
data=qm.get_data()
|
data = qm.get_data()
|
||||||
row_headers=qm.get_headers()
|
row_headers = qm.get_headers()
|
||||||
|
|
||||||
logger.debug("query done")
|
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")
|
logger.warning("no data found")
|
||||||
|
|
||||||
payload=[]
|
payload = []
|
||||||
for result in data:
|
for result in data:
|
||||||
# create dictionary from recorset
|
# create dictionary from recorset
|
||||||
main_result=dict(zip(row_headers,result))
|
main_result = dict(zip(row_headers, result))
|
||||||
# find the country in prefix table
|
# 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
|
# merge recordset and contry prefix
|
||||||
main_result["country"]=search_prefix["country"]
|
main_result["country"] = search_prefix["country"]
|
||||||
main_result["iso"]=search_prefix["iso"]
|
main_result["iso"] = search_prefix["iso"]
|
||||||
|
|
||||||
payload.append({**main_result})
|
payload.append({**main_result})
|
||||||
|
|
||||||
return payload
|
return payload
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
|
|
||||||
#find adxo events
|
|
||||||
adxo_events=None
|
# find adxo events
|
||||||
|
adxo_events = None
|
||||||
|
|
||||||
|
|
||||||
def get_adxo():
|
def get_adxo():
|
||||||
global adxo_events
|
global adxo_events
|
||||||
adxo_events=get_adxo_events()
|
adxo_events = get_adxo_events()
|
||||||
threading.Timer(12*3600,get_adxo).start()
|
threading.Timer(12 * 3600, get_adxo).start()
|
||||||
|
|
||||||
|
|
||||||
get_adxo()
|
get_adxo()
|
||||||
|
|
||||||
#create data provider for charts
|
# create data provider for charts
|
||||||
heatmap_cbp=ContinentsBandsProvider(logger,qm,continents_cq,band_frequencies)
|
heatmap_cbp = ContinentsBandsProvider(logger, qm, continents_cq, band_frequencies)
|
||||||
bar_graph_spm=SpotsPerMounthProvider(logger,qm)
|
bar_graph_spm = SpotsPerMounthProvider(logger, qm)
|
||||||
line_graph_st=SpotsTrend(logger,qm)
|
line_graph_st = SpotsTrend(logger, qm)
|
||||||
bubble_graph_hb=HourBand(logger,qm,band_frequencies)
|
bubble_graph_hb = HourBand(logger, qm, band_frequencies)
|
||||||
geo_graph_wdsl=WorldDxSpotsLive(logger,qm,pfxt)
|
geo_graph_wdsl = WorldDxSpotsLive(logger, qm, pfxt)
|
||||||
|
|
||||||
#ROUTINGS
|
# ROUTINGS
|
||||||
@app.route('/spotlist', methods=['GET'])
|
@app.route("/spotlist", methods=["GET"])
|
||||||
def spotlist():
|
def spotlist():
|
||||||
response=flask.Response(json.dumps(spotquery()))
|
response = flask.Response(json.dumps(spotquery()))
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def who_is_connected():
|
def who_is_connected():
|
||||||
host_port=cfg['telnet'].split(':')
|
host_port = cfg["telnet"].split(":")
|
||||||
response=who(host_port[0],host_port[1],cfg['mycallsign'])
|
response = who(host_port[0], host_port[1], cfg["mycallsign"])
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.route('/', methods=['GET'])
|
|
||||||
@app.route('/index.html', methods=['GET'])
|
@app.route("/", methods=["GET"])
|
||||||
|
@app.route("/index.html", methods=["GET"])
|
||||||
def spots():
|
def spots():
|
||||||
#payload=spotquery()
|
# 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))
|
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
|
return response
|
||||||
|
|
||||||
@app.route('/service-worker.js', methods=['GET'])
|
|
||||||
|
@app.route("/service-worker.js", methods=["GET"])
|
||||||
def sw():
|
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():
|
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():
|
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():
|
def plots():
|
||||||
whoj=who_is_connected()
|
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))
|
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
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/cookies.html', methods=['GET'])
|
@app.route("/cookies.html", methods=["GET"])
|
||||||
def cookies():
|
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
|
return response
|
||||||
|
|
||||||
@app.route('/privacy.html', methods=['GET'])
|
|
||||||
|
@app.route("/privacy.html", methods=["GET"])
|
||||||
def privacy():
|
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
|
return response
|
||||||
|
|
||||||
@app.route('/sitemap.xml')
|
|
||||||
|
@app.route("/sitemap.xml")
|
||||||
def sitemap():
|
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():
|
def callsign():
|
||||||
#payload=spotquery()
|
# payload=spotquery()
|
||||||
callsign=request.args.get('c')
|
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))
|
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
|
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():
|
def find_callsign():
|
||||||
callsign=request.args.get('c')
|
callsign = request.args.get("c")
|
||||||
response=pfxt.find(callsign)
|
response = pfxt.find(callsign)
|
||||||
if response is None:
|
if response is None:
|
||||||
response=flask.Response(status=204)
|
response = flask.Response(status=204)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.route('/plot_get_heatmap_data', methods=['GET'])
|
|
||||||
|
@app.route("/plot_get_heatmap_data", methods=["GET"])
|
||||||
def get_heatmap_data():
|
def get_heatmap_data():
|
||||||
continent=request.args.get('continent')
|
continent = request.args.get("continent")
|
||||||
response=flask.Response(json.dumps(heatmap_cbp.get_data(continent)))
|
response = flask.Response(json.dumps(heatmap_cbp.get_data(continent)))
|
||||||
logger.debug(response)
|
logger.debug(response)
|
||||||
if response is None:
|
if response is None:
|
||||||
response=flask.Response(status=204)
|
response = flask.Response(status=204)
|
||||||
return response
|
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():
|
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)
|
logger.debug(response)
|
||||||
if response is None:
|
if response is None:
|
||||||
response=flask.Response(status=204)
|
response = flask.Response(status=204)
|
||||||
return response
|
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():
|
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)
|
logger.debug(response)
|
||||||
if response is None:
|
if response is None:
|
||||||
response=flask.Response(status=204)
|
response = flask.Response(status=204)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.route('/plot_get_hour_band', methods=['GET'])
|
|
||||||
|
@app.route("/plot_get_hour_band", methods=["GET"])
|
||||||
def get_dx_hour_band():
|
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)
|
logger.debug(response)
|
||||||
if response is None:
|
if response is None:
|
||||||
response=flask.Response(status=204)
|
response = flask.Response(status=204)
|
||||||
return response
|
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():
|
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)
|
logger.debug(response)
|
||||||
if response is None:
|
if response is None:
|
||||||
response=flask.Response(status=204)
|
response = flask.Response(status=204)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_template_scope():
|
def inject_template_scope():
|
||||||
injections = dict()
|
injections = dict()
|
||||||
|
|
||||||
def cookies_check():
|
def cookies_check():
|
||||||
value = request.cookies.get('cookie_consent')
|
value = request.cookies.get("cookie_consent")
|
||||||
return value == 'true'
|
return value == "true"
|
||||||
|
|
||||||
injections.update(cookies_check=cookies_check)
|
injections.update(cookies_check=cookies_check)
|
||||||
return injections
|
return injections
|
||||||
|
|
||||||
|
|
||||||
@app.after_request
|
@app.after_request
|
||||||
def add_security_headers(resp):
|
def add_security_headers(resp):
|
||||||
resp.headers['Strict-Transport-Security']='max-age=1000'
|
resp.headers["Strict-Transport-Security"] = "max-age=1000"
|
||||||
resp.headers['X-Xss-Protection']='1; mode=block'
|
resp.headers["X-Xss-Protection"] = "1; mode=block"
|
||||||
resp.headers['X-Frame-Options']='SAMEORIGIN'
|
resp.headers["X-Frame-Options"] = "SAMEORIGIN"
|
||||||
resp.headers['X-Content-Type-Options']='nosniff'
|
resp.headers["X-Content-Type-Options"] = "nosniff"
|
||||||
resp.headers['Referrer-Policy']='strict-origin-when-cross-origin'
|
resp.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
||||||
resp.headers['Cache-Control']='public, no-cache'
|
resp.headers["Cache-Control"] = "public, no-cache"
|
||||||
resp.headers['Pragma']='no-cache'
|
resp.headers["Pragma"] = "no-cache"
|
||||||
|
|
||||||
resp.headers['Content-Security-Policy']="\
|
resp.headers[
|
||||||
|
"Content-Security-Policy"
|
||||||
|
] = "\
|
||||||
default-src 'self';\
|
default-src 'self';\
|
||||||
script-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
script-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
||||||
style-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
|
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 'sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx' 'sha512-uvXdJud8WaOlQFjlz9B15Yy2Au/bMAvz79F7Xa6OakCl2jvQPdHD0hb3dEqZRdSwG4/sknePXlE7GiarwA/9Wg==';\
|
||||||
# style-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
# style-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
||||||
|
|
||||||
#script-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline'
|
# script-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline'
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(host='0.0.0.0')
|
app.run(host="0.0.0.0")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user