mirror of
https://github.com/coulisse/spiderweb.git
synced 2024-09-21 07:27:09 +00:00
build script
This commit is contained in:
parent
0a85e94787
commit
c780bc4650
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,4 +3,4 @@
|
||||
config.json
|
||||
__init__
|
||||
__pycache__
|
||||
|
||||
.vscode/
|
||||
|
61
README.md
61
README.md
@ -13,7 +13,7 @@
|
||||
- **Release:** v2.4.1
|
||||
- **Author:** Corrado Gerbaldo - [IU1BOW](https://www.qrz.com/db/IU1BOW)
|
||||
- **Mail:** <corrado.gerbaldo@gmail.com>
|
||||
- **Licensing:** Gpl V3.0 see ["LICENSE"](LICENSE) file.
|
||||
- **Licensing:** Gpl V3.0 see [LICENSE](LICENSE) file.
|
||||
- **Languages:** This application is written in Python 3.11/flask,Javascript and HTML
|
||||
|
||||
___
|
||||
@ -233,12 +233,69 @@ This application is designed for desktop and mobile phone. It is a [PWA](https:/
|
||||
|
||||
### API
|
||||
**Spot list**
|
||||
|
||||
You can retrive last spots calling "**/spotlist**"; For example [www.iu1bow.com/spotlist](https://www.iu1bow.com/spotlist)
|
||||
|
||||
**country of a callsign**
|
||||
**Country of a callsign**
|
||||
|
||||
You cam retrive some informations about a callsign with **callsign**; For example [www.iu1bow.com/callsign?c=IU1BOW](https://www.iu1bow.com/callsign?c=IU1BOW)
|
||||
|
||||
|
||||
### Development
|
||||
**Directory structure**
|
||||
```
|
||||
/ . main application files
|
||||
├── cfg . configuration files (put here your config.json with your setting)
|
||||
├── docs . documentation
|
||||
├── lib . python libs used for the application
|
||||
├── log . application log
|
||||
├── scripts . utility scripts for testing, build etc.
|
||||
├── static . static files css, js, data, html, images etc.
|
||||
│ ├── css .
|
||||
│ │ ├── dev . development css not minifyed/uglifyed
|
||||
│ │ └── rel . release css minifyed/uglifyed (do not change these files)
|
||||
│ ├── data . application data (world.json)
|
||||
│ ├── html .
|
||||
│ │ └── dev . html templates used for build release static html (for offline)
|
||||
│ │ └── rel . release static html (for offline)
|
||||
│ ├── images . static images
|
||||
│ │ └── icons . static icons
|
||||
│ └── js .
|
||||
│ ├── dev . development js not minifyed/uglifyed
|
||||
│ └── rel . release js minifyed/uglifyed (do not change these files)
|
||||
└── templates . html templates used by python flask for serve the web pages
|
||||
```
|
||||
**Application description**
|
||||
|
||||
The main **server** application ```webapp.py``` is in the root folder. In this application there are routing to html dynamic templates and serves also back-end API. This is whapped by ```wsgi.py``` for using with **bjoern** server.
|
||||
|
||||
Static files (css, js...) are in ```static``` directory: here there are subdirectories:
|
||||
- ```dev``` where you can edit and modify sources
|
||||
- ```rel``` here there are release files created with the building process and used in producion
|
||||
|
||||
**Lint**
|
||||
|
||||
For lint javascript I use **ESLint**. You can install with ```npm init @eslint/config```
|
||||
pylint ```pip install pylint```
|
||||
|
||||
**Building process**
|
||||
|
||||
Prerequisites:
|
||||
| **Component** | **Description** | **Install command** |
|
||||
|---------------|----------------------------------------------------|---------------------------------|
|
||||
| npm | a packet manager for javascript | depend on your os. See [official page](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) |
|
||||
| uglify-js | npm component used to minify and uglify javascript | ```npm install uglify-js -g``` |
|
||||
| css-minify | npm component used to minify css | ```npm install css-minify -g``` |
|
||||
| staticjinja | python module used to create static page starting from a html template | ```pip install staticjinja``` |
|
||||
|
||||
|
||||
You can build the software for test (dev), or for production (release) environements.
|
||||
In ```scripts``` directory launch:
|
||||
- ```./build.sh -d``` for dev environment
|
||||
|
||||
- ```./build.sh -r``` for release environment
|
||||
|
||||
|
||||
### Screenshots
|
||||
----------
|
||||
|
||||
|
@ -12,6 +12,7 @@ level=INFO
|
||||
handlers=stream_handler,file_handler
|
||||
|
||||
[handler_stream_handler]
|
||||
level=INFO
|
||||
class=StreamHandler
|
||||
formatter=formatter
|
||||
args=(sys.stderr,)
|
||||
|
@ -1,13 +1,15 @@
|
||||
### Change log
|
||||
Date: 02/01/2023
|
||||
Release v.2.4
|
||||
Date: 07/01/2023
|
||||
Release: v2.4.1
|
||||
- changed dimensions of spots in world dx spost charts
|
||||
- managed empty data in data providers for charts
|
||||
- removed jQuery: migrated to vanilla javascript
|
||||
- for spot refresh get only new spots starting from last rowid
|
||||
- modified building script
|
||||
- moved cty file in data directory
|
||||
___
|
||||
Date: 01/01/2023
|
||||
Release v.2.4
|
||||
Release: v2.4
|
||||
- migration to python 3.11
|
||||
- added descriptions to continents
|
||||
- fixed issue #23: Wrong flag for Cocos (Keeling) Islands
|
||||
|
52
lib/adxo.py
52
lib/adxo.py
@ -2,44 +2,50 @@
|
||||
# Module used to get Announced DX Operation from NG3K website via .ICS (Calendar)
|
||||
# file, parse it and return a dictionary with these events
|
||||
# ***********************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import requests
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import tempfile
|
||||
|
||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||
datefmt="%m/%d/%Y %I:%M:%S",
|
||||
)
|
||||
# format single line
|
||||
def format_line(prop):
|
||||
prop_out = dict()
|
||||
try:
|
||||
dtstart=datetime.strptime(prop['DTSTART;VALUE=DATE'], '%Y%m%d')
|
||||
dtend=datetime.strptime(prop['DTEND;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")
|
||||
now = datetime.now()
|
||||
if dtstart <= now and dtend >= now:
|
||||
prop_out['start']=dtstart.strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||
prop_out['end']=dtend.strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||
prop_out['summary']=prop['SUMMARY'].split('(')[0].strip()
|
||||
prop_out['callsign']=prop['SUMMARY'].split('(',1)[1].split(')',1)[0]
|
||||
prop_out['description']=prop['DESCRIPTION'].replace('\\', '')
|
||||
prop_out["start"] = dtstart.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
prop_out["end"] = dtend.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
prop_out["summary"] = prop["SUMMARY"].split("(")[0].strip()
|
||||
prop_out["callsign"] = prop["SUMMARY"].split("(", 1)[1].split(")", 1)[0]
|
||||
prop_out["description"] = prop["DESCRIPTION"].replace("\\", "")
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return prop_out
|
||||
|
||||
|
||||
# TODO: url from conf parameter
|
||||
|
||||
|
||||
def get_adxo_events():
|
||||
url = 'http://dxcal.kj4z.com/dxcal'
|
||||
url = "http://dxcal.kj4z.com/dxcal"
|
||||
line_num = 0
|
||||
event_num = 0
|
||||
try:
|
||||
logging.info('connection to: '+url)
|
||||
logging.info("connection to: " + url)
|
||||
req = requests.get(url)
|
||||
events = []
|
||||
prop = dict()
|
||||
prop_name=''
|
||||
prop_name = ""
|
||||
with tempfile.TemporaryFile() as temp:
|
||||
temp.write(req.content)
|
||||
temp.seek(0)
|
||||
@ -47,18 +53,18 @@ def get_adxo_events():
|
||||
for line_bytes in lines:
|
||||
line = line_bytes.decode()
|
||||
line_num += 1
|
||||
current_line_array=line.strip().split(':', 1)
|
||||
if current_line_array[0]=='BEGIN':
|
||||
if current_line_array[1]=='VCALENDAR':
|
||||
current_line_array = line.strip().split(":", 1)
|
||||
if current_line_array[0] == "BEGIN":
|
||||
if current_line_array[1] == "VCALENDAR":
|
||||
prop = {}
|
||||
if current_line_array[1]=='VEVENT':
|
||||
if current_line_array[1] == "VEVENT":
|
||||
event_num += 1
|
||||
prop = {}
|
||||
else:
|
||||
if current_line_array[0]=='END':
|
||||
if current_line_array[1]=='VCALENDAR':
|
||||
if current_line_array[0] == "END":
|
||||
if current_line_array[1] == "VCALENDAR":
|
||||
pass
|
||||
if current_line_array[1]=='VEVENT':
|
||||
if current_line_array[1] == "VEVENT":
|
||||
prop = format_line(prop)
|
||||
if prop:
|
||||
events.append(prop)
|
||||
@ -68,10 +74,12 @@ def get_adxo_events():
|
||||
prop[prop_name] = current_line_array[1]
|
||||
else:
|
||||
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.info('number ADXO events: '+str(event_num))
|
||||
logging.debug("number of line reads: " + str(line_num))
|
||||
logging.info("number ADXO events: " + str(event_num))
|
||||
return events
|
||||
except Exception as e1:
|
||||
logging.error(e1)
|
||||
|
259
lib/config.py
259
lib/config.py
@ -1,54 +1,58 @@
|
||||
# *************************************************************************************
|
||||
# CLI Utility used for manage configuration file
|
||||
# *************************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import os
|
||||
import os.path
|
||||
from os import path
|
||||
import json
|
||||
|
||||
configs = [('mycallsign','Callsign_______________: '),
|
||||
('mysql/host','MySql host_____________: '),
|
||||
('mysql/db','MySql database_________: '),
|
||||
('mysql/user','MySql user_____________: '),
|
||||
('mysql/passwd','MySql password_________: '),
|
||||
('timer/interval','Spot page refresh(ms)__: ' ),
|
||||
('plot_refresh_timer/interval','Plot page refresh(ms)__: '),
|
||||
('mail','Mail address___________: '),
|
||||
('mail_token','token google 2FA auth__: '),
|
||||
('telnet','Telnet address_________: '),
|
||||
('enable_cq_filter','Enable cq filter______: ')
|
||||
configs = [
|
||||
("mycallsign", "Callsign_______________: "),
|
||||
("mysql/host", "MySql host_____________: "),
|
||||
("mysql/db", "MySql database_________: "),
|
||||
("mysql/user", "MySql user_____________: "),
|
||||
("mysql/passwd", "MySql password_________: "),
|
||||
("timer/interval", "Spot page refresh(ms)__: "),
|
||||
("plot_refresh_timer/interval", "Plot page refresh(ms)__: "),
|
||||
("mail", "Mail address___________: "),
|
||||
("mail_token", "token google 2FA auth__: "),
|
||||
("telnet", "Telnet address_________: "),
|
||||
("enable_cq_filter", "Enable cq filter______: "),
|
||||
]
|
||||
|
||||
class bcolors:
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
TEMPLATE_FILE = '../cfg/config.json.template'
|
||||
USER_FILE = '../cfg/config.json'
|
||||
class bcolors:
|
||||
HEADER = "\033[95m"
|
||||
OKBLUE = "\033[94m"
|
||||
OKGREEN = "\033[92m"
|
||||
WARNING = "\033[93m"
|
||||
FAIL = "\033[91m"
|
||||
ENDC = "\033[0m"
|
||||
BOLD = "\033[1m"
|
||||
UNDERLINE = "\033[4m"
|
||||
|
||||
|
||||
TEMPLATE_FILE = "../cfg/config.json.template"
|
||||
USER_FILE = "../cfg/config.json"
|
||||
# search and open the configuration file
|
||||
def get_cfg_file(template):
|
||||
if template:
|
||||
cfg_file = TEMPLATE_FILE
|
||||
if not path.exists(cfg_file):
|
||||
print ('file not found')
|
||||
cfg_file = ''
|
||||
print("file not found")
|
||||
cfg_file = ""
|
||||
else:
|
||||
cfg_file = USER_FILE
|
||||
if not path.exists(cfg_file):
|
||||
cfg_file = TEMPLATE_FILE
|
||||
if not path.exists(cfg_file):
|
||||
cfg_file = ''
|
||||
cfg_file = ""
|
||||
|
||||
print('Configuration file loaded from: '+cfg_file)
|
||||
print("Configuration file loaded from: " + cfg_file)
|
||||
return cfg_file
|
||||
|
||||
|
||||
# covert file in json
|
||||
def get_cfg_json(f):
|
||||
if f:
|
||||
@ -59,10 +63,11 @@ def get_cfg_json(f):
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
# read a single value from json
|
||||
def get_cfg_value(cfg, key):
|
||||
|
||||
k_arr = key.split('/')
|
||||
k_arr = key.split("/")
|
||||
l_arr = len(k_arr)
|
||||
|
||||
try:
|
||||
@ -71,12 +76,13 @@ def get_cfg_value(cfg,key):
|
||||
elif l_arr == 2:
|
||||
val = cfg[k_arr[0]][k_arr[1]]
|
||||
except KeyError:
|
||||
val=''
|
||||
val = ""
|
||||
|
||||
return val
|
||||
|
||||
|
||||
def set_cfg_value(cfg, key, val):
|
||||
k_arr = key.split('/')
|
||||
k_arr = key.split("/")
|
||||
l_arr = len(k_arr)
|
||||
|
||||
try:
|
||||
@ -89,113 +95,130 @@ def set_cfg_value(cfg,key,val):
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def style_field(lbl, val):
|
||||
return lbl + bcolors.BOLD + str(val) + bcolors.ENDC
|
||||
|
||||
|
||||
def show_menu(cfg, key):
|
||||
menu=get_cfg_value(cfg,'menu/menu_list')
|
||||
menu = get_cfg_value(cfg, "menu/menu_list")
|
||||
i = -1
|
||||
for element in menu:
|
||||
i += 1
|
||||
print(style_field(str(i)+'. external: ',str(element['external']))+style_field(', label: ',element['label']))
|
||||
print(style_field(' link____: ',element['link']))
|
||||
print(
|
||||
style_field(str(i) + ". external: ", str(element["external"]))
|
||||
+ style_field(", label: ", element["label"])
|
||||
)
|
||||
print(style_field(" link____: ", element["link"]))
|
||||
print()
|
||||
return
|
||||
|
||||
|
||||
def help_list():
|
||||
print()
|
||||
print (' h: help')
|
||||
print (' vc: view config.')
|
||||
print (' ec: edit config.')
|
||||
print (' vm: view menu')
|
||||
print (' em: edit menu')
|
||||
print (' s: save')
|
||||
print (' t: load config. from template')
|
||||
print(" h: help")
|
||||
print(" vc: view config.")
|
||||
print(" ec: edit config.")
|
||||
print(" vm: view menu")
|
||||
print(" em: edit menu")
|
||||
print(" s: save")
|
||||
print(" t: load config. from template")
|
||||
print()
|
||||
print (' x: exit')
|
||||
print(" x: exit")
|
||||
print()
|
||||
return
|
||||
|
||||
|
||||
def help_menu_edit():
|
||||
print()
|
||||
print (' n: new menu entry')
|
||||
print (' d: delete menu entry')
|
||||
print (' e: edit menu entry')
|
||||
print(" n: new menu entry")
|
||||
print(" d: delete menu entry")
|
||||
print(" e: edit menu entry")
|
||||
print()
|
||||
print (' x: exit')
|
||||
print(" x: exit")
|
||||
print()
|
||||
return
|
||||
|
||||
|
||||
def view(cfg, t):
|
||||
if t == 'c':
|
||||
if t == "c":
|
||||
i = 0
|
||||
for element in configs:
|
||||
(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
|
||||
elif t == 'm':
|
||||
print ('Menu:')
|
||||
show_menu(cfg,'menu/menu_list')
|
||||
elif t == "m":
|
||||
print("Menu:")
|
||||
show_menu(cfg, "menu/menu_list")
|
||||
|
||||
print()
|
||||
return
|
||||
|
||||
|
||||
def user_input(caption):
|
||||
return input(caption)
|
||||
|
||||
|
||||
def edit_config(cfg):
|
||||
view(cfg,'c')
|
||||
inp=''
|
||||
while inp!='x':
|
||||
inp=str(user_input ('Type the number of config. you would to edit, x for end: ')).lower()
|
||||
view(cfg, "c")
|
||||
inp = ""
|
||||
while inp != "x":
|
||||
inp = str(
|
||||
user_input("Type the number of config. you would to edit, x for end: ")
|
||||
).lower()
|
||||
if inp.isdigit():
|
||||
inp = int(inp)
|
||||
try:
|
||||
(key, lbl) = configs[inp]
|
||||
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:
|
||||
print ('configuration not found!')
|
||||
print("configuration not found!")
|
||||
finally:
|
||||
if val!='x' and val !='':
|
||||
if val != "x" and val != "":
|
||||
cfg = set_cfg_value(cfg, key, val)
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def menu_delete_entry(cfg):
|
||||
view(cfg,'m')
|
||||
inp=''
|
||||
while inp!='x':
|
||||
inp=str(user_input ('Choose the menu you would to delete, x for end: ')).lower()
|
||||
view(cfg, "m")
|
||||
inp = ""
|
||||
while inp != "x":
|
||||
inp = str(
|
||||
user_input("Choose the menu you would to delete, x for end: ")
|
||||
).lower()
|
||||
if inp.isdigit():
|
||||
inp = int(inp)
|
||||
element = cfg['menu']['menu_list']
|
||||
element = cfg["menu"]["menu_list"]
|
||||
try:
|
||||
del element[inp]
|
||||
cfg['menu']['menu_list']=element
|
||||
cfg["menu"]["menu_list"] = element
|
||||
except IndexError:
|
||||
print ('menu entry not found!')
|
||||
print("menu entry not found!")
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def is_external(val):
|
||||
return val == 'y'
|
||||
return val == "y"
|
||||
|
||||
|
||||
def menu_input_entry(entry, new_entry):
|
||||
if not new_entry:
|
||||
print('label old value: '+entry['label'])
|
||||
print("label old value: " + entry["label"])
|
||||
|
||||
label=str(user_input('label new value: '))
|
||||
label = str(user_input("label new value: "))
|
||||
if not new_entry:
|
||||
print('link old value: '+entry['link'])
|
||||
print("link old value: " + entry["link"])
|
||||
|
||||
link=str(user_input('link value: '))
|
||||
link = str(user_input("link value: "))
|
||||
if not new_entry:
|
||||
print('external old value: '+str(entry['external']))
|
||||
print("external old value: " + str(entry["external"]))
|
||||
|
||||
external = ''
|
||||
while external != 'y' and external != 'n':
|
||||
external=str(user_input('open link external [y/n]: ')).lower()
|
||||
external = ""
|
||||
while external != "y" and external != "n":
|
||||
external = str(user_input("open link external [y/n]: ")).lower()
|
||||
|
||||
# if external == 'y':
|
||||
# external = True
|
||||
@ -203,96 +226,104 @@ def menu_input_entry(entry,new_entry):
|
||||
# external = False
|
||||
external = is_external(external)
|
||||
|
||||
entry['label']=label
|
||||
entry['link']=link
|
||||
entry['external']=external
|
||||
entry["label"] = label
|
||||
entry["link"] = link
|
||||
entry["external"] = external
|
||||
|
||||
return entry
|
||||
|
||||
|
||||
def menu_edit_entry(cfg):
|
||||
view(cfg,'m')
|
||||
inp=''
|
||||
while inp!='x':
|
||||
inp=str(user_input ('Choose the menu you would to edit, X for end: ')).lower()
|
||||
view(cfg, "m")
|
||||
inp = ""
|
||||
while inp != "x":
|
||||
inp = str(user_input("Choose the menu you would to edit, X for end: ")).lower()
|
||||
if inp.isdigit():
|
||||
inp = int(inp)
|
||||
element = cfg['menu']['menu_list']
|
||||
element = cfg["menu"]["menu_list"]
|
||||
try:
|
||||
element[inp] = menu_input_entry(element[inp], False)
|
||||
cfg['menu']['menu_list']=element
|
||||
cfg["menu"]["menu_list"] = element
|
||||
except IndexError:
|
||||
print ('menu entry not found!')
|
||||
print("menu entry not found!")
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def menu_new_entry(cfg):
|
||||
view(cfg,'m')
|
||||
inp=''
|
||||
view(cfg, "m")
|
||||
inp = ""
|
||||
valid = False
|
||||
entry = menu_input_entry({"label": "", "link": "", "external": False}, True)
|
||||
while not valid:
|
||||
inp=str(user_input ('Enter the position number of your menu entry, X for end: '))
|
||||
inp = str(
|
||||
user_input("Enter the position number of your menu entry, X for end: ")
|
||||
)
|
||||
if inp.isdigit():
|
||||
inp = int(inp)
|
||||
if inp > len(cfg['menu']['menu_list']):
|
||||
print('position not valid!')
|
||||
if inp > len(cfg["menu"]["menu_list"]):
|
||||
print("position not valid!")
|
||||
valid = False
|
||||
elif inp == len(cfg['menu']['menu_list']):
|
||||
cfg['menu']['menu_list'].append(entry)
|
||||
elif inp == len(cfg["menu"]["menu_list"]):
|
||||
cfg["menu"]["menu_list"].append(entry)
|
||||
valid = True
|
||||
else:
|
||||
cfg['menu']['menu_list'].insert(inp,entry)
|
||||
cfg["menu"]["menu_list"].insert(inp, entry)
|
||||
valid = True
|
||||
else:
|
||||
valid = False
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def edit_menu(cfg):
|
||||
view(cfg,'m')
|
||||
inp=''
|
||||
while inp!='x':
|
||||
view(cfg, "m")
|
||||
inp = ""
|
||||
while inp != "x":
|
||||
help_menu_edit()
|
||||
inp=str(user_input ('Edit menu> make your choiche: ')).lower()
|
||||
if (inp == 'n'):
|
||||
inp = str(user_input("Edit menu> make your choiche: ")).lower()
|
||||
if inp == "n":
|
||||
cfg = menu_new_entry(cfg)
|
||||
elif inp == 'd':
|
||||
elif inp == "d":
|
||||
cfg = menu_delete_entry(cfg)
|
||||
elif inp == 'e':
|
||||
elif inp == "e":
|
||||
cfg = menu_edit_entry(cfg)
|
||||
|
||||
return 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)
|
||||
print ('configuration saved to: '+USER_FILE)
|
||||
print("configuration saved to: " + USER_FILE)
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
print()
|
||||
print('*** DxSpider configuration ***')
|
||||
print("*** DxSpider configuration ***")
|
||||
finput = get_cfg_file(False)
|
||||
help_list()
|
||||
cfg = get_cfg_json(finput)
|
||||
inp = ''
|
||||
while inp != 'x' and inp != 'exit':
|
||||
inp = str(user_input('Main> make your choiche: ')).lower()
|
||||
if (inp == 'h' or inp =='?' or inp =='help'):
|
||||
inp = ""
|
||||
while inp != "x" and inp != "exit":
|
||||
inp = str(user_input("Main> make your choiche: ")).lower()
|
||||
if inp == "h" or inp == "?" or inp == "help":
|
||||
help_list()
|
||||
elif inp == 'vc':
|
||||
view(cfg,'c')
|
||||
elif inp == 'vm':
|
||||
view(cfg,'m')
|
||||
elif inp == 'ec':
|
||||
elif inp == "vc":
|
||||
view(cfg, "c")
|
||||
elif inp == "vm":
|
||||
view(cfg, "m")
|
||||
elif inp == "ec":
|
||||
cfg = edit_config(cfg)
|
||||
elif inp == 'em':
|
||||
elif inp == "em":
|
||||
cfg = edit_menu(cfg)
|
||||
elif inp == 's' or inp == 'save':
|
||||
elif inp == "s" or inp == "save":
|
||||
save_cfg(cfg)
|
||||
elif inp == 't':
|
||||
elif inp == "t":
|
||||
finput = get_cfg_file(True)
|
||||
cfg = get_cfg_json(finput)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
167
lib/cty.py
167
lib/cty.py
@ -1,7 +1,7 @@
|
||||
# *************************************************************************************
|
||||
# Module used to download cty.dat country file and search callsign in it
|
||||
# *************************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import requests
|
||||
import logging
|
||||
import os
|
||||
@ -10,34 +10,40 @@ from threading import Timer
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||
datefmt="%m/%d/%Y %I:%M:%S",
|
||||
)
|
||||
# TODO: url from conf parameter
|
||||
url = 'https://www.country-files.com/cty/cty_wt_mod.dat'
|
||||
cty_local=os.path.dirname(__file__)+'/../cfg/cty_wt_mod.dat'
|
||||
country_file=os.path.dirname(__file__)+'/../cfg/country.json'
|
||||
url = "https://www.country-files.com/cty/cty_wt_mod.dat"
|
||||
cty_local = os.path.dirname(__file__) + "/../static/data/cty_wt_mod.dat"
|
||||
country_file = os.path.dirname(__file__) + "/../cfg/country.json"
|
||||
# -------------------------------------------------------------------------------------
|
||||
# download country files cty.dat
|
||||
# -------------------------------------------------------------------------------------
|
||||
def download_cty(url, cty_local):
|
||||
try:
|
||||
logging.info('connection to: '+url)
|
||||
logging.info("connection to: " + url)
|
||||
req = requests.get(url)
|
||||
f=open(cty_local,'wb')
|
||||
f = open(cty_local, "wb")
|
||||
f.write(req.content)
|
||||
f.close()
|
||||
logging.info('cty file saved in: '+cty_local)
|
||||
logging.info("cty file saved in: " + cty_local)
|
||||
return 0
|
||||
|
||||
except Exception as e1:
|
||||
logging.error(e1)
|
||||
return 1
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# get age of a file in days
|
||||
# -------------------------------------------------------------------------------------
|
||||
def file_age_in_days(pathname):
|
||||
return (time.time() - os.stat(pathname).st_ctime) / (24 * 3600)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# manage file cty.dat
|
||||
# -------------------------------------------------------------------------------------
|
||||
@ -45,20 +51,31 @@ def get_cty(url,local):
|
||||
if os.path.isfile(local):
|
||||
age = file_age_in_days(local)
|
||||
if age > 7:
|
||||
logging.info(cty_local+' too old ('+str(round(age,0))+' days): proceding to download it')
|
||||
logging.info(
|
||||
cty_local
|
||||
+ " too old ("
|
||||
+ str(round(age, 0))
|
||||
+ " days): proceding to download it"
|
||||
)
|
||||
return download_cty(url, local)
|
||||
# else:
|
||||
# logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary to download it')
|
||||
# return 0
|
||||
logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary to download it')
|
||||
logging.info(
|
||||
cty_local
|
||||
+ " updated ("
|
||||
+ str(round(age, 0))
|
||||
+ " days), is not necessary to download it"
|
||||
)
|
||||
return 0
|
||||
# else:
|
||||
# logging.info(cty_local+' not present: proceding to download it')
|
||||
# return download_cty(url,local)
|
||||
|
||||
logging.info(cty_local+' not present: proceding to download it')
|
||||
logging.info(cty_local + " not present: proceding to download it")
|
||||
return download_cty(url, local)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# parsing alias and get exceptions
|
||||
# -------------------------------------------------------------------------------------
|
||||
@ -67,11 +84,11 @@ def parse_alias(alias,master):
|
||||
try:
|
||||
# create a dictionary of array, with start and end position of each exception
|
||||
find_dict = {}
|
||||
find_dict['pos_cq']=[alias.find('('),alias.find(')')]
|
||||
find_dict['pos_itu']=[alias.find('['),alias.find(']')]
|
||||
find_dict['pos_lat_lon']=[alias.find('<'),alias.find('>')]
|
||||
find_dict['pos_continent']=[alias.find('{'),alias.find('}')]
|
||||
find_dict['pos_time']=[alias.find('~'),alias[:alias.find('~')].find('~')]
|
||||
find_dict["pos_cq"] = [alias.find("("), alias.find(")")]
|
||||
find_dict["pos_itu"] = [alias.find("["), alias.find("]")]
|
||||
find_dict["pos_lat_lon"] = [alias.find("<"), alias.find(">")]
|
||||
find_dict["pos_continent"] = [alias.find("{"), alias.find("}")]
|
||||
find_dict["pos_time"] = [alias.find("~"), alias[: alias.find("~")].find("~")]
|
||||
|
||||
first = 9999
|
||||
parsed = {}
|
||||
@ -88,45 +105,51 @@ def parse_alias(alias,master):
|
||||
parsed["darc_waedc"] = master["darc_waedc"]
|
||||
|
||||
# extract override cq
|
||||
if find_dict['pos_cq'][0]>=0:
|
||||
parsed["cq"]=alias[find_dict['pos_cq'][0]+1:find_dict['pos_cq'][1]]
|
||||
if find_dict['pos_cq'][0] < first:
|
||||
first=find_dict['pos_cq'][0]
|
||||
if find_dict["pos_cq"][0] >= 0:
|
||||
parsed["cq"] = alias[find_dict["pos_cq"][0] + 1 : find_dict["pos_cq"][1]]
|
||||
if find_dict["pos_cq"][0] < first:
|
||||
first = find_dict["pos_cq"][0]
|
||||
|
||||
# extract override itu
|
||||
if find_dict['pos_itu'][0]>=0:
|
||||
parsed["itu"]=alias[find_dict['pos_itu'][0]+1:find_dict['pos_itu'][1]]
|
||||
if find_dict['pos_itu'][0] < first:
|
||||
first=find_dict['pos_itu'][0]
|
||||
if find_dict["pos_itu"][0] >= 0:
|
||||
parsed["itu"] = alias[find_dict["pos_itu"][0] + 1 : find_dict["pos_itu"][1]]
|
||||
if find_dict["pos_itu"][0] < first:
|
||||
first = find_dict["pos_itu"][0]
|
||||
|
||||
# extract override lat_lon
|
||||
if find_dict['pos_lat_lon'][0]>=0:
|
||||
lat_lon=alias[find_dict['pos_lat_lon'][0]+1:find_dict['pos_lat_lon'][1]]
|
||||
parsed["lat"]=lat_lon[0:].split('/')[0]
|
||||
parsed["lon"]=lat_lon[:len(lat_lon)].split('/')[1]
|
||||
if find_dict['pos_lat_lon'][0] < first:
|
||||
first=find_dict['pos_lat_lon'][0]
|
||||
if find_dict["pos_lat_lon"][0] >= 0:
|
||||
lat_lon = alias[
|
||||
find_dict["pos_lat_lon"][0] + 1 : find_dict["pos_lat_lon"][1]
|
||||
]
|
||||
parsed["lat"] = lat_lon[0:].split("/")[0]
|
||||
parsed["lon"] = lat_lon[: len(lat_lon)].split("/")[1]
|
||||
if find_dict["pos_lat_lon"][0] < first:
|
||||
first = find_dict["pos_lat_lon"][0]
|
||||
|
||||
# extract override continent
|
||||
if find_dict['pos_continent'][0]>=0:
|
||||
parsed["continent"]=alias[find_dict['pos_continent'][0]+1:find_dict['pos_continent'][1]]
|
||||
if find_dict['pos_continent'][0] < first:
|
||||
first=find_dict['pos_continent'][0]
|
||||
if find_dict["pos_continent"][0] >= 0:
|
||||
parsed["continent"] = alias[
|
||||
find_dict["pos_continent"][0] + 1 : find_dict["pos_continent"][1]
|
||||
]
|
||||
if find_dict["pos_continent"][0] < first:
|
||||
first = find_dict["pos_continent"][0]
|
||||
|
||||
# extract override time
|
||||
if find_dict['pos_time'][0]>=0:
|
||||
parsed["time_loc"]=alias[find_dict['pos_time'][0]+1:find_dict['pos_time'][1]]
|
||||
if find_dict['pos_time'][0] < first:
|
||||
first=find_dict['pos_time'][0]
|
||||
if find_dict["pos_time"][0] >= 0:
|
||||
parsed["time_loc"] = alias[
|
||||
find_dict["pos_time"][0] + 1 : find_dict["pos_time"][1]
|
||||
]
|
||||
if find_dict["pos_time"][0] < first:
|
||||
first = find_dict["pos_time"][0]
|
||||
|
||||
# extract callsign
|
||||
callsing = alias[:first].upper()
|
||||
if callsing.startswith('='):
|
||||
parsed["full"]='y'
|
||||
if callsing.startswith("="):
|
||||
parsed["full"] = "y"
|
||||
callsing = callsing[1:]
|
||||
|
||||
if callsing.startswith('*'):
|
||||
parsed["darc_waedc"]='y'
|
||||
if callsing.startswith("*"):
|
||||
parsed["darc_waedc"] = "y"
|
||||
callsing = callsing[1:]
|
||||
|
||||
return callsing, parsed
|
||||
@ -138,6 +161,7 @@ def parse_alias(alias,master):
|
||||
logging.error(alias)
|
||||
return -1
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# load file from configuration, containing all world country, with related ISO codes
|
||||
# -------------------------------------------------------------------------------------
|
||||
@ -145,6 +169,7 @@ def load_country():
|
||||
with open(country_file) as json_country:
|
||||
return json.load(json_country)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# search for ISO code, transcoding the country description
|
||||
# -------------------------------------------------------------------------------------
|
||||
@ -152,18 +177,20 @@ def add_country(table):
|
||||
country_data = load_country()
|
||||
for key, value in table.items():
|
||||
found = 0
|
||||
for i in country_data['country_codes']:
|
||||
if i['desc'].upper()==value['country'].upper():
|
||||
for i in country_data["country_codes"]:
|
||||
if i["desc"].upper() == value["country"].upper():
|
||||
value["iso"] = i["ISO"]
|
||||
value["wpx"] = i["WPX"]
|
||||
found = 1
|
||||
break
|
||||
if found == 0:
|
||||
logging.warning('country "'+value['country']+'" not found in cfg/country.json')
|
||||
logging.warning(
|
||||
'country "' + value["country"] + '" not found in cfg/country.json'
|
||||
)
|
||||
return
|
||||
|
||||
class prefix_table:
|
||||
|
||||
class prefix_table:
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# init of the class
|
||||
@ -212,6 +239,7 @@ class prefix_table:
|
||||
return
|
||||
|
||||
global initialization
|
||||
|
||||
def initialization():
|
||||
refresh()
|
||||
global timer
|
||||
@ -219,26 +247,28 @@ class prefix_table:
|
||||
timer.start()
|
||||
return
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# refresh data
|
||||
# -------------------------------------------------------------------------------------
|
||||
global refresh
|
||||
|
||||
def refresh():
|
||||
|
||||
logging.info('CTY: start initialization')
|
||||
logging.info("CTY: start initialization")
|
||||
if get_cty(url, cty_local) > 0:
|
||||
logging.error('there is a problem during downloading country files!')
|
||||
logging.info('continue with previous file')
|
||||
logging.info('check the connectivity, or put manually the file '+cty_local)
|
||||
logging.error("there is a problem during downloading country files!")
|
||||
logging.info("continue with previous file")
|
||||
logging.info(
|
||||
"check the connectivity, or put manually the file " + cty_local
|
||||
)
|
||||
line_num = 0
|
||||
line_num_valid = 0
|
||||
entities_number = 0
|
||||
data=''
|
||||
data = ""
|
||||
table = []
|
||||
prefix_master.clear()
|
||||
try:
|
||||
with open(cty_local, 'r') as f:
|
||||
with open(cty_local, "r") as f:
|
||||
for line in f:
|
||||
line_num += 1
|
||||
li = line.strip()
|
||||
@ -246,15 +276,15 @@ class prefix_table:
|
||||
if not li.startswith("#"):
|
||||
line_num_valid += 1
|
||||
data += li
|
||||
logging.info('number of lines reads: '+str(line_num))
|
||||
logging.info('number of valid lines: '+str(line_num_valid))
|
||||
logging.info("number of lines reads: " + str(line_num))
|
||||
logging.info("number of valid lines: " + str(line_num_valid))
|
||||
|
||||
# split in array of lines terminated with semicolon
|
||||
table=data.split(';')
|
||||
table = data.split(";")
|
||||
for i, item_table in enumerate(table):
|
||||
row=item_table.split(':')
|
||||
row = item_table.split(":")
|
||||
# remove trailing spaces and uppercasing
|
||||
row = [x.strip(' ') for x in row]
|
||||
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
|
||||
@ -266,21 +296,23 @@ class prefix_table:
|
||||
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'
|
||||
single_prefix["full"] = "n"
|
||||
single_prefix["darc_waedc"] = "n"
|
||||
prefix_master[row[7].upper()] = single_prefix
|
||||
# managing sub-prefixies
|
||||
sub_prefixies=row[8].split(',')
|
||||
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)))
|
||||
logging.info("number of entities: " + str(entities_number))
|
||||
logging.info("number of single alias: " + str(len(prefix_master)))
|
||||
add_country(prefix_master)
|
||||
logging.info('memory used for prefix: '+str(prefix_master.__sizeof__())+' bytes')
|
||||
logging.info('CTY: initialization complete')
|
||||
logging.info(
|
||||
"memory used for prefix: " + str(prefix_master.__sizeof__()) + " bytes"
|
||||
)
|
||||
logging.info("CTY: initialization complete")
|
||||
return
|
||||
|
||||
except Exception as e1:
|
||||
@ -289,7 +321,6 @@ class prefix_table:
|
||||
logging.error(message)
|
||||
return
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# find a callsign
|
||||
# -------------------------------------------------------------------------------------
|
||||
@ -302,7 +333,7 @@ class prefix_table:
|
||||
while i > 0:
|
||||
try:
|
||||
data = prefix_master[callsign[:i]]
|
||||
data['match']=callsign[:i]
|
||||
data["match"] = callsign[:i]
|
||||
return data
|
||||
except KeyError:
|
||||
pass
|
||||
@ -321,5 +352,5 @@ class prefix_table:
|
||||
|
||||
def __del__(self):
|
||||
timer.cancel()
|
||||
logging.info('prefix_table destroyed')
|
||||
logging.info("prefix_table destroyed")
|
||||
return
|
||||
|
@ -1,22 +1,23 @@
|
||||
# *************************************************************************************
|
||||
# Module used to interface with telnet cluster and get connected nodes
|
||||
# *************************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
|
||||
import telnetlib
|
||||
import struct
|
||||
import json
|
||||
import logging
|
||||
|
||||
|
||||
def parse_who(lines):
|
||||
# print(lines.decode('ascii'))
|
||||
|
||||
# create a list o lines and define the structure
|
||||
lines = lines.splitlines()
|
||||
fmtstring='2x 9s 10s 18s 9s 8s 15s'
|
||||
fmtstring = "2x 9s 10s 18s 9s 8s 15s"
|
||||
fieldstruct = struct.Struct(fmtstring)
|
||||
|
||||
row_headers=('callsign','type','started','name','average_rtt','link')
|
||||
row_headers = ("callsign", "type", "started", "name", "average_rtt", "link")
|
||||
|
||||
# skip first lines and last line
|
||||
payload = []
|
||||
@ -24,8 +25,8 @@ def parse_who(lines):
|
||||
line = lines[i]
|
||||
ln = len(line)
|
||||
|
||||
padding=bytes(' ' * (struct.calcsize(fmtstring)-ln),'utf-8')
|
||||
line=(line+padding)
|
||||
padding = bytes(" " * (struct.calcsize(fmtstring) - ln), "utf-8")
|
||||
line = line + padding
|
||||
|
||||
if ln > 10:
|
||||
parse = fieldstruct.unpack_from
|
||||
@ -33,7 +34,7 @@ def parse_who(lines):
|
||||
|
||||
for j, item_field in enumerate(fields):
|
||||
try:
|
||||
fields[j]=item_field.decode('utf-8').strip()
|
||||
fields[j] = item_field.decode("utf-8").strip()
|
||||
except AttributeError:
|
||||
print(item_field)
|
||||
payload.append(dict(zip(row_headers, fields)))
|
||||
@ -42,9 +43,10 @@ def parse_who(lines):
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
def who(host, port, user):
|
||||
|
||||
WAIT_FOR = b'dxspider >'
|
||||
WAIT_FOR = b"dxspider >"
|
||||
TIMEOUT = 1
|
||||
res = 0
|
||||
|
||||
@ -52,23 +54,25 @@ def who(host,port,user):
|
||||
tn = telnetlib.Telnet(host, port, TIMEOUT)
|
||||
try:
|
||||
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)
|
||||
tn.write(b"who\n")
|
||||
res = tn.read_until(WAIT_FOR, TIMEOUT)
|
||||
tn.write(b"exit\n")
|
||||
|
||||
except EOFError:
|
||||
logging.error ('could not autenticate to telnet dxspider host: check user callsign ')
|
||||
logging.error(
|
||||
"could not autenticate to telnet dxspider host: check user callsign "
|
||||
)
|
||||
logging.error(res)
|
||||
res = 0
|
||||
except:
|
||||
logging.error ('could not connect to telnet dxspider host: check host/port')
|
||||
ret = ''
|
||||
logging.error("could not connect to telnet dxspider host: check host/port")
|
||||
ret = ""
|
||||
|
||||
if res != 0:
|
||||
ret = parse_who(res)
|
||||
else:
|
||||
ret=''
|
||||
ret = ""
|
||||
|
||||
return ret
|
||||
|
@ -3,7 +3,7 @@
|
||||
# spiderweb
|
||||
# you can use it in build step
|
||||
# *************************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
@ -12,19 +12,23 @@ import json
|
||||
import re
|
||||
import sys
|
||||
|
||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||
datefmt="%m/%d/%Y %I:%M:%S",
|
||||
)
|
||||
# dxspider_band='/home/sysop/spider/data/bands.pl'
|
||||
output_modes='../cfg/modes.json'
|
||||
output_modes = "../cfg/modes.json"
|
||||
# -------------------------------------------------------------------------------------
|
||||
# reads bands file and convert to json
|
||||
# -------------------------------------------------------------------------------------
|
||||
def parse(input_file):
|
||||
line_num = 0
|
||||
line_num_valid = 0
|
||||
data=''
|
||||
data = ""
|
||||
band_trigger = False
|
||||
try:
|
||||
with open(input_file, 'r') as f:
|
||||
with open(input_file, "r") as f:
|
||||
for line in f:
|
||||
line_num += 1
|
||||
li = line.strip()
|
||||
@ -45,11 +49,13 @@ def parse(input_file):
|
||||
data = data.replace("bless", "")
|
||||
data = data.replace("%bands=", "")
|
||||
data = data.replace(",'bands'", "")
|
||||
data=re.sub(r"([\"'])(?:(?=(\\?))\2.)*?\1=>", "", data) #remove token like '136khz' =>
|
||||
data = re.sub(
|
||||
r"([\"'])(?:(?=(\\?))\2.)*?\1=>", "", data
|
||||
) # remove token like '136khz' =>
|
||||
data = data.replace("=>", ":")
|
||||
data = data.replace("(", "")
|
||||
data = data.replace(")", "")
|
||||
data=re.sub(r'([a-zA-Z]+):', r'"\1":', data) #add quotation around words
|
||||
data = re.sub(r"([a-zA-Z]+):", r'"\1":', data) # add quotation around words
|
||||
data = data.replace(",}", "}")
|
||||
data = "[" + data + "]"
|
||||
data = data.replace(",]", "]")
|
||||
@ -65,16 +71,19 @@ def parse(input_file):
|
||||
logging.error(input_file)
|
||||
return ""
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# add min max freq element to the related mode in final json
|
||||
# -------------------------------------------------------------------------------------
|
||||
def add_freq(mode, freq, json_modes):
|
||||
try:
|
||||
for modes in json_modes['modes']:
|
||||
for modes in json_modes["modes"]:
|
||||
if modes["id"] == mode:
|
||||
ind_freq = 0
|
||||
while ind_freq < len(freq):
|
||||
modes["freq"].append({"min":freq[ind_freq],"max":freq[ind_freq+1]})
|
||||
modes["freq"].append(
|
||||
{"min": freq[ind_freq], "max": freq[ind_freq + 1]}
|
||||
)
|
||||
ind_freq += 2
|
||||
|
||||
return json_modes
|
||||
@ -85,12 +94,15 @@ def add_freq(mode, freq, json_modes):
|
||||
logging.error(message)
|
||||
return {}
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
# reads bands file and convert to json
|
||||
# -------------------------------------------------------------------------------------
|
||||
def create_output(data):
|
||||
|
||||
json_modes=json.loads('{"modes":[{"id":"cw","freq":[]},{"id":"phone","freq":[]},{"id":"digi","freq":[]}]}')
|
||||
json_modes = json.loads(
|
||||
'{"modes":[{"id":"cw","freq":[]},{"id":"phone","freq":[]},{"id":"digi","freq":[]}]}'
|
||||
)
|
||||
|
||||
try:
|
||||
for element in data:
|
||||
@ -115,10 +127,11 @@ def create_output(data):
|
||||
logging.error(input_file)
|
||||
return {}
|
||||
|
||||
|
||||
# *************************************************************************************
|
||||
# Main
|
||||
# *************************************************************************************
|
||||
logging.info('RDxSpider band file conversion starting...')
|
||||
logging.info("RDxSpider band file conversion starting...")
|
||||
if len(sys.argv) != 2:
|
||||
logging.error("argument invalid. Specify dxcluster band file")
|
||||
logging.error("use: python get_dxcluster_modes.py input_file")
|
||||
@ -129,9 +142,9 @@ dxspider_band=sys.argv[1]
|
||||
parsed = parse(dxspider_band)
|
||||
if 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)
|
||||
logging.info('modes saved to: '+output_modes)
|
||||
logging.info('DxSpider band file conversion completed')
|
||||
logging.info("modes saved to: " + output_modes)
|
||||
logging.info("DxSpider band file conversion completed")
|
||||
else:
|
||||
logging.error("error on parsing input file")
|
||||
|
@ -1,7 +1,7 @@
|
||||
# ***********************************************************************************
|
||||
# Module that contain classess for providing data to plotting front-end page
|
||||
# ***********************************************************************************
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import threading
|
||||
import time
|
||||
from lib.qry import query_manager
|
||||
@ -42,11 +42,11 @@ class BaseDataProvider:
|
||||
self.logger.info("Class: %s init end", self.__class__.__name__)
|
||||
return
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Class for managing data for Continent/Band chart
|
||||
# -----------------------------------------------------------------------------------
|
||||
class ContinentsBandsProvider(BaseDataProvider):
|
||||
|
||||
def __init__(self, logger, qm, continents, bands):
|
||||
# Calling constructor of base class
|
||||
super().__init__(logger, qm, continents, bands)
|
||||
@ -57,27 +57,45 @@ class ContinentsBandsProvider(BaseDataProvider):
|
||||
self.logger.info("doing query...")
|
||||
|
||||
# construct bands query
|
||||
bands_qry_string = 'CASE '
|
||||
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 += (
|
||||
" WHEN freq between "
|
||||
+ str(band_frequencies["bands"][i]["min"])
|
||||
+ " AND "
|
||||
+ str(band_frequencies["bands"][i]["max"])
|
||||
)
|
||||
bands_qry_string += ' THEN "' + band_frequencies["bands"][i]["id"] + '"'
|
||||
|
||||
# construct continent region query
|
||||
spottercq_qry_string = 'CASE '
|
||||
spotcq_qry_string = 'CASE '
|
||||
spottercq_qry_string = "CASE "
|
||||
spotcq_qry_string = "CASE "
|
||||
for i in range(len(continents_cq["continents"])):
|
||||
spottercq_qry_string+=' WHEN spottercq in('+continents_cq["continents"][i]["cq"]+')'
|
||||
spottercq_qry_string+=' THEN "' +continents_cq["continents"][i]["id"]+'"'
|
||||
spotcq_qry_string+=' WHEN spotcq in('+continents_cq["continents"][i]["cq"]+')'
|
||||
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 ="""
|
||||
qry_string = (
|
||||
"""
|
||||
SELECT
|
||||
"""+spottercq_qry_string+""" ELSE spottercq END,
|
||||
"""+spotcq_qry_string+""" ELSE spotcq END,
|
||||
"""+bands_qry_string+""" END,
|
||||
"""
|
||||
+ spottercq_qry_string
|
||||
+ """ ELSE spottercq END,
|
||||
"""
|
||||
+ spotcq_qry_string
|
||||
+ """ ELSE spotcq END,
|
||||
"""
|
||||
+ bands_qry_string
|
||||
+ """ END,
|
||||
count(0) number
|
||||
from spot
|
||||
where
|
||||
@ -86,6 +104,7 @@ class ContinentsBandsProvider(BaseDataProvider):
|
||||
group by 1, 2, 3
|
||||
;
|
||||
"""
|
||||
)
|
||||
|
||||
self.logger.debug(qry_string)
|
||||
self.qm.qry(qry_string)
|
||||
@ -116,7 +135,10 @@ class ContinentsBandsProvider(BaseDataProvider):
|
||||
for k, item_band in enumerate(band_list):
|
||||
found = 0
|
||||
for lis, item_filtered in enumerate(data_filtered):
|
||||
if item_filtered[0]==item_continent["id"] and item_filtered[1]==item_band["id"]:
|
||||
if (
|
||||
item_filtered[0] == item_continent["id"]
|
||||
and item_filtered[1] == item_band["id"]
|
||||
):
|
||||
# cartesian_product.append(item_filtered)
|
||||
element = []
|
||||
element.append(j)
|
||||
@ -141,7 +163,9 @@ class ContinentsBandsProvider(BaseDataProvider):
|
||||
qry_data = self.__load_data(self.bands, self.continents)
|
||||
for i, item in enumerate(self.continents["continents"]):
|
||||
continent = item["id"]
|
||||
data_de=self.__normalize_continent(qry_data,continent,self.continents["continents"],self.bands["bands"])
|
||||
data_de = self.__normalize_continent(
|
||||
qry_data, continent, self.continents["continents"], self.bands["bands"]
|
||||
)
|
||||
lcl_data.update({continent: data_de})
|
||||
|
||||
self.glb_data = lcl_data
|
||||
@ -155,11 +179,11 @@ class ContinentsBandsProvider(BaseDataProvider):
|
||||
self.glb_response.update({"band activity": self.glb_data[continent_filter]})
|
||||
return self.glb_response
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Class for managing data for Spots per months chart
|
||||
# -----------------------------------------------------------------------------------
|
||||
class SpotsPerMounthProvider(BaseDataProvider):
|
||||
|
||||
def __init__(self, logger, qm):
|
||||
# Calling constructor of base class
|
||||
super().__init__(logger, qm, [], [])
|
||||
@ -243,7 +267,7 @@ class SpotsPerMounthProvider(BaseDataProvider):
|
||||
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]}
|
||||
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)
|
||||
@ -251,19 +275,23 @@ class SpotsPerMounthProvider(BaseDataProvider):
|
||||
self.glb_data = lcl_data
|
||||
self.glb_last_refresh = time.time()
|
||||
|
||||
threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time
|
||||
threading.Timer(
|
||||
60 * 60 * 24, self.refresh
|
||||
).start() # periodic refresh: set time
|
||||
return
|
||||
|
||||
def get_data(self,):
|
||||
def get_data(
|
||||
self,
|
||||
):
|
||||
super().get_data()
|
||||
self.glb_response.update({"spots_per_month": self.glb_data})
|
||||
return self.glb_response
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Class for managing data for Spots trend chart
|
||||
# -----------------------------------------------------------------------------------
|
||||
class SpotsTrend(BaseDataProvider):
|
||||
|
||||
def __init__(self, logger, qm):
|
||||
# Calling constructor of base class
|
||||
super().__init__(logger, qm, [], [])
|
||||
@ -294,11 +322,13 @@ class SpotsTrend(BaseDataProvider):
|
||||
self.logger.warning("no data found")
|
||||
|
||||
# normalize data eliminating peaks
|
||||
df['day']=pd.to_datetime(df['day'])
|
||||
df=df.set_index('day')
|
||||
df=df.resample('D').interpolate(method='pad', limit_direction='forward', axis=0)
|
||||
df=df.rolling('30D').mean()
|
||||
df['total']=df['total'].round(0)
|
||||
df["day"] = pd.to_datetime(df["day"])
|
||||
df = df.set_index("day")
|
||||
df = df.resample("D").interpolate(
|
||||
method="pad", limit_direction="forward", axis=0
|
||||
)
|
||||
df = df.rolling("30D").mean()
|
||||
df["total"] = df["total"].round(0)
|
||||
|
||||
return df
|
||||
|
||||
@ -310,14 +340,16 @@ class SpotsTrend(BaseDataProvider):
|
||||
|
||||
# iterate panda dataframe
|
||||
for index, row in qry_data.iterrows():
|
||||
lcl_data.update({str(index.date()):row['total']})
|
||||
lcl_data.update({str(index.date()): row["total"]})
|
||||
|
||||
self.logger.debug(lcl_data)
|
||||
|
||||
self.glb_data = lcl_data
|
||||
self.glb_last_refresh = time.time()
|
||||
|
||||
threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time
|
||||
threading.Timer(
|
||||
60 * 60 * 24, self.refresh
|
||||
).start() # periodic refresh: set time
|
||||
return
|
||||
|
||||
def get_data(self):
|
||||
@ -325,11 +357,11 @@ class SpotsTrend(BaseDataProvider):
|
||||
self.glb_response.update({"spots_trend": self.glb_data})
|
||||
return self.glb_response
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Class for managing data for Hour/Band chart
|
||||
# -----------------------------------------------------------------------------------
|
||||
class HourBand(BaseDataProvider):
|
||||
|
||||
def __init__(self, logger, qm, bands):
|
||||
# Calling constructor of base class
|
||||
super().__init__(logger, qm, [], bands)
|
||||
@ -341,24 +373,41 @@ class HourBand(BaseDataProvider):
|
||||
|
||||
self.logger.debug(self.bands)
|
||||
# construct bands query
|
||||
bands_qry_string = 'CASE '
|
||||
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 += (
|
||||
" WHEN freq between "
|
||||
+ str(self.bands["bands"][i]["min"])
|
||||
+ " AND "
|
||||
+ str(self.bands["bands"][i]["max"])
|
||||
)
|
||||
bands_qry_string += ' THEN "' + self.bands["bands"][i]["id"] + '"'
|
||||
|
||||
# construct bands query weight
|
||||
bands_weight_qry_string = 'CASE '
|
||||
bands_weight_qry_string = "CASE "
|
||||
for i in range(len(self.bands["bands"])):
|
||||
bands_weight_qry_string+=' WHEN freq between '+str(self.bands["bands"][i]["min"])+' AND '+ str(self.bands["bands"][i]["max"])
|
||||
bands_weight_qry_string+=' THEN "'+str(self.bands["bands"][i]["min"])+'"'
|
||||
bands_weight_qry_string += (
|
||||
" WHEN freq between "
|
||||
+ str(self.bands["bands"][i]["min"])
|
||||
+ " AND "
|
||||
+ str(self.bands["bands"][i]["max"])
|
||||
)
|
||||
bands_weight_qry_string += (
|
||||
' THEN "' + str(self.bands["bands"][i]["min"]) + '"'
|
||||
)
|
||||
|
||||
# construct final query string
|
||||
qry_string ="""
|
||||
qry_string = (
|
||||
"""
|
||||
select s1.band, s1.hour, s1.total from (
|
||||
SELECT
|
||||
cast(concat(HOUR (FROM_UNIXTIME(time))) as unsigned) as hour,
|
||||
"""+bands_qry_string+""" ELSE "other" END as band,
|
||||
cast("""+bands_weight_qry_string+""" ELSE 0 END as unsigned) as band_weight,
|
||||
"""
|
||||
+ bands_qry_string
|
||||
+ """ ELSE "other" END as band,
|
||||
cast("""
|
||||
+ bands_weight_qry_string
|
||||
+ """ ELSE 0 END as unsigned) as band_weight,
|
||||
count(0) AS total
|
||||
from spot
|
||||
WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL 1 MONTH)
|
||||
@ -368,6 +417,7 @@ class HourBand(BaseDataProvider):
|
||||
order by s1.band, s1.hour
|
||||
;
|
||||
"""
|
||||
)
|
||||
|
||||
self.logger.debug(qry_string)
|
||||
self.qm.qry(qry_string)
|
||||
@ -395,7 +445,9 @@ class HourBand(BaseDataProvider):
|
||||
self.glb_data = lcl_data
|
||||
self.glb_last_refresh = time.time()
|
||||
|
||||
threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time
|
||||
threading.Timer(
|
||||
60 * 60 * 24, self.refresh
|
||||
).start() # periodic refresh: set time
|
||||
return
|
||||
|
||||
def get_data(self):
|
||||
@ -440,7 +492,7 @@ class WorldDxSpotsLive(BaseDataProvider):
|
||||
self.logger.debug(data)
|
||||
|
||||
# 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 = []
|
||||
lat = []
|
||||
lon = []
|
||||
@ -459,10 +511,10 @@ class WorldDxSpotsLive(BaseDataProvider):
|
||||
idx += 1
|
||||
row_id.append(idx)
|
||||
|
||||
df['dx']=dx
|
||||
df['lat']=lat
|
||||
df['lon']=lon
|
||||
df['row_id']=row_id
|
||||
df["dx"] = dx
|
||||
df["lat"] = lat
|
||||
df["lon"] = lon
|
||||
df["row_id"] = row_id
|
||||
df_grp = df.groupby(["lat", "lon"])["row_id"].count().reset_index(name="count")
|
||||
|
||||
if df is None == 0:
|
||||
@ -479,7 +531,7 @@ class WorldDxSpotsLive(BaseDataProvider):
|
||||
|
||||
lcl_data = []
|
||||
for index, row in qry_data.iterrows():
|
||||
record=dict(lat=row['lat'], lon=row['lon'], count=row['count'])
|
||||
record = dict(lat=row["lat"], lon=row["lon"], count=row["count"])
|
||||
lcl_data.append(record)
|
||||
|
||||
self.logger.debug(lcl_data)
|
||||
|
34
lib/qry.py
34
lib/qry.py
@ -9,38 +9,44 @@ import logging
|
||||
import json
|
||||
import pandas as pd
|
||||
|
||||
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S')
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s]: %(message)s",
|
||||
datefmt="%m/%d/%Y %I:%M:%S",
|
||||
)
|
||||
|
||||
|
||||
class query_manager:
|
||||
# connection definition
|
||||
|
||||
def __init__(self):
|
||||
try:
|
||||
with open('../cfg/config.json') as json_data_file:
|
||||
with open("../cfg/config.json") as json_data_file:
|
||||
cfg = json.load(json_data_file)
|
||||
except Exception as e1:
|
||||
logging.info(e1)
|
||||
logging.info('trying with other path...')
|
||||
logging.info("trying with other path...")
|
||||
try:
|
||||
with open ('cfg/config.json') as json_data_file:
|
||||
with open("cfg/config.json") as json_data_file:
|
||||
cfg = json.load(json_data_file)
|
||||
except Exception as e2:
|
||||
logging.error(e2)
|
||||
return
|
||||
|
||||
logging.info('config file loaded')
|
||||
self.__cnxpool = pooling.MySQLConnectionPool(host=cfg['mysql']['host'],
|
||||
user=cfg['mysql']['user'],
|
||||
passwd=cfg['mysql']['passwd'],
|
||||
db=cfg['mysql']['db'],
|
||||
charset='latin1',
|
||||
logging.info("config file loaded")
|
||||
self.__cnxpool = pooling.MySQLConnectionPool(
|
||||
host=cfg["mysql"]["host"],
|
||||
user=cfg["mysql"]["user"],
|
||||
passwd=cfg["mysql"]["passwd"],
|
||||
db=cfg["mysql"]["db"],
|
||||
charset="latin1",
|
||||
# charset='utf8mb4',
|
||||
# collation = 'utf8mb4_general_ci',
|
||||
pool_name="spider_pool",
|
||||
use_pure=True,
|
||||
pool_size = 3
|
||||
pool_size=3,
|
||||
)
|
||||
logging.info('db connection pool created')
|
||||
logging.info("db connection pool created")
|
||||
|
||||
# normal query
|
||||
def qry(self, qs, prepared_statement=False):
|
||||
@ -49,7 +55,9 @@ class query_manager:
|
||||
cursor = cnx.cursor(prepared=prepared_statement)
|
||||
cursor.execute(qs)
|
||||
self.__data = cursor.fetchall()
|
||||
self.__row_headers=[x[0] for x in cursor.description] #this will extract row headers
|
||||
self.__row_headers = [
|
||||
x[0] for x in cursor.description
|
||||
] # this will extract row headers
|
||||
cursor.close()
|
||||
except Exception as e2:
|
||||
logging.error(e2)
|
||||
|
@ -1,15 +1,20 @@
|
||||
#
|
||||
# little script used to build static pages
|
||||
#
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
from staticjinja import Site
|
||||
|
||||
|
||||
def cookies_check():
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
site = Site.make_site(searchpath="../static/html/templates/",outpath="../static/html/",env_globals={
|
||||
'cookies_check':cookies_check,
|
||||
})
|
||||
site = Site.make_site(
|
||||
searchpath="../static/html/dev/",
|
||||
outpath="../static/html/rel/",
|
||||
env_globals={
|
||||
"cookies_check": cookies_check,
|
||||
},
|
||||
)
|
||||
site.render(use_reloader=False)
|
||||
|
@ -1 +1 @@
|
||||
nohup ./test.sh >/dev/null 2>&1 &
|
||||
nohup ./test.sh -r >/dev/null 2>&1 &
|
||||
|
@ -1,5 +1,7 @@
|
||||
astroid==2.12.14
|
||||
charset-normalizer==2.1.1
|
||||
click==8.1.3
|
||||
dill==0.3.6
|
||||
docopt-ng==0.8.1
|
||||
easywatch==0.0.5
|
||||
Flask==2.2.2
|
||||
@ -7,14 +9,18 @@ Flask-Minify==0.41
|
||||
Flask-WTF==1.0.1
|
||||
htmlmin==0.1.12
|
||||
idna==3.4
|
||||
isort==5.11.4
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.2
|
||||
jsmin==3.0.1
|
||||
lazy-object-proxy==1.9.0
|
||||
lesscpy==0.15.1
|
||||
MarkupSafe==2.1.1
|
||||
mccabe==0.7.0
|
||||
mysql-connector-python==8.0.31
|
||||
numpy==1.24.1
|
||||
pandas==1.5.2
|
||||
platformdirs==2.6.2
|
||||
ply==3.11
|
||||
protobuf==4.21.12
|
||||
python-dateutil==2.8.2
|
||||
@ -22,8 +28,10 @@ pytz==2022.7
|
||||
rcssmin==1.1.1
|
||||
requests==2.28.1
|
||||
six==1.16.0
|
||||
tomlkit==0.11.6
|
||||
urllib3==1.26.13
|
||||
watchdog==2.2.0
|
||||
Werkzeug==2.2.2
|
||||
wrapt==1.14.1
|
||||
WTForms==3.0.1
|
||||
xxhash==3.1.0
|
||||
|
185
scripts/build.sh
185
scripts/build.sh
@ -8,13 +8,141 @@ path_static='../static'
|
||||
path_static_html=${path_static}'/html'
|
||||
path_static_js=${path_static}'/js'
|
||||
path_static_css=${path_static}'/css'
|
||||
path_cfg='../cfg'
|
||||
app_ini=${path_cfg}'/webapp_log_config.ini'
|
||||
path_docs='../docs'
|
||||
readme='../README.md'
|
||||
manifest=${path_static}'/manifest.webmanifest'
|
||||
changelog=${path_docs}'/'CHANGELOG.md
|
||||
|
||||
#echo '*** SPIDERWEB building process ***'
|
||||
html_change_references(){
|
||||
for i in ${path_templates}/*.html
|
||||
do
|
||||
echo "changing references to scripts in ${i}"
|
||||
if [ "${1}" == "-r" ]; then
|
||||
# concatenating 2 sed command with ";"
|
||||
# 1) if found static/js/dev/ replace .js with .min.js
|
||||
# 2) replace static/js/dev/ with static/js/rel/
|
||||
if ! sed -i '/static\/js\/dev/s/\.js/\.min\.js/;s/static\/js\/dev/static\/js\/rel/g' ${i}; then
|
||||
echo 'ERROR replacing .js to .min.js '
|
||||
exit 6
|
||||
fi
|
||||
# the same but for css
|
||||
if ! sed -i '/static\/css\/dev/s/\.css/\.min\.css/;s/static\/css\/dev/static\/css\/rel/g' ${i}; then
|
||||
echo 'ERROR replacing .css to .min.css '
|
||||
exit 6
|
||||
fi
|
||||
elif [ "${1}" == "-d" ]; then
|
||||
# concatenating 2 sed command with ";"
|
||||
# 1) if found static/js/rel/ replace .min.js with .js
|
||||
# 2) replace static/js/rel/ with static/js/dev/
|
||||
if ! sed -i '/static\/js\/rel/s/\.min\.js/\.js/;s/static\/js\/rel/static\/js\/dev/g' ${i}; then
|
||||
echo 'ERROR replacing .min.js to .js'
|
||||
exit 6
|
||||
fi
|
||||
# the same but for css
|
||||
if ! sed -i '/static\/css\/rel/s/\.min\.css/\.css/;s/static\/css\/rel/static\/css\/dev/g' ${i}; then
|
||||
echo 'ERROR replacing .min.css to .css'
|
||||
exit 6
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
if [ "$1" != "-r" ] && [ "$1" != "-d" ]; then
|
||||
echo 'wrong options:'
|
||||
echo ' -d: debug'
|
||||
echo ' -r: release'
|
||||
exit 5
|
||||
fi
|
||||
|
||||
echo '*** SPIDERWEB building process ***'
|
||||
if [ "$1" == "-r" ]; then
|
||||
echo 'creating RELEASE application'
|
||||
|
||||
#used to minify the application javascript
|
||||
echo 'minify javascripts...'
|
||||
shopt -s extglob
|
||||
rm ${path_static_js}/rel/*.js
|
||||
for i in ${path_static_js}/dev/*.js
|
||||
do
|
||||
[[ -e ${i} ]] || break # handle the case of no files found
|
||||
file_no_ext=$(basename "${i%.js}")
|
||||
out=${path_static_js}/rel/${file_no_ext}.min.js
|
||||
echo "${i} --> ${out}"
|
||||
if ! uglifyjs --compress --mangle -- ${i} > ${out}
|
||||
then
|
||||
echo 'ERROR minifying javascript: '${i}
|
||||
shopt -u extglob
|
||||
exit 80
|
||||
fi
|
||||
if [ ! -s "${out}" ]; then
|
||||
echo "File is empty"
|
||||
shopt -u extglob
|
||||
exit 81
|
||||
fi
|
||||
done
|
||||
|
||||
#used to minify css
|
||||
echo 'minify css...'
|
||||
rm ${path_static_css}/rel/*.css
|
||||
for i in ${path_static_css}/dev/*.css
|
||||
do
|
||||
[[ -e ${i} ]] || break # handle the case of no files found
|
||||
echo ${i}
|
||||
file_no_ext=$(basename "${i%.css}")
|
||||
out=${path_static_css}/rel/${file_no_ext}.min.css
|
||||
echo "${i} --> ${out}"
|
||||
#if ! curl -X POST -s --fail --compressed --data "code_type=css" --data-urlencode 'code@'${i} https://htmlcompressor.com/compress > ${path_static_css}/rel/${file_no_ext}.min.css
|
||||
if ! css-minify -f ${i} -o ${path_static_css}/rel/
|
||||
then
|
||||
echo 'ERROR minifying css: ' ${out}
|
||||
shopt -u extglob
|
||||
exit 90
|
||||
fi
|
||||
if [ ! -s "${out}" ]; then
|
||||
echo "File is empty"
|
||||
shopt -u extglob
|
||||
exit 91
|
||||
fi
|
||||
#sleep 5
|
||||
done
|
||||
shopt -u extglob
|
||||
html_change_references -r
|
||||
|
||||
echo 'writing requirements...'
|
||||
if ! pip freeze|tee ../requirements.txt
|
||||
then
|
||||
echo 'ERROR wrinting requirements'
|
||||
exit 60
|
||||
fi
|
||||
|
||||
echo 'remove some packages from requirements...'
|
||||
sed -i '/certifi==/d' ../requirements.txt
|
||||
sed -i '/staticjinja==/d' ../requirements.txt
|
||||
|
||||
if ! sed -i 's/level=DEBUG/level=INFO/g' ${app_ini}; then
|
||||
echo 'ERROR settimg loglevel=INFO '
|
||||
exit 12
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [ "$1" == "-d" ]; then
|
||||
echo 'creating DEBUG application'
|
||||
html_change_references -d
|
||||
|
||||
if ! sed -i 's/level=INFO/level=DEBUG/g' ${app_ini}; then
|
||||
echo 'ERROR settimg loglevel=DEBUG '
|
||||
exit 12
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
|
||||
#echo 'create modes.json from dxspider bands.pl'
|
||||
|
||||
#python ../lib/get_dxcluster_modes.py /home/sysop/spider/data/bands.pl
|
||||
#if [ "$?" != "0" ]; then
|
||||
# echo 'ERROR on creating modes.json from dxspider bands.pl'
|
||||
@ -55,7 +183,7 @@ then
|
||||
fi
|
||||
|
||||
echo 'writing date in '${changelog} '...'
|
||||
#sed -i '1,4s/Date: [0-9][0-9]\/[0-9][0-9]\/[0-9][0-9][0-9][0-9]/Date: '`date '+%d\/%m\/%Y'`'/g' ${changelog}
|
||||
|
||||
if ! sed -i '1,4s/Date: [0-9][0-9]\/[0-9][0-9]\/[0-9][0-9][0-9][0-9]/Date: '$(date '+%d\/%m\/%Y')'/g' ${changelog}
|
||||
then
|
||||
echo 'ERROR writing date in '${changelog}
|
||||
@ -76,58 +204,5 @@ then
|
||||
exit 50
|
||||
fi
|
||||
|
||||
echo 'writing requirements...'
|
||||
if ! pip freeze|tee ../requirements.txt
|
||||
then
|
||||
echo 'ERROR wrinting requirements'
|
||||
exit 60
|
||||
fi
|
||||
|
||||
echo 'remove some packages...'
|
||||
sed -i '/certifi==/d' ../requirements.txt
|
||||
sed -i '/staticjinja==/d' ../requirements.txt
|
||||
|
||||
#used to minify the application javascript
|
||||
echo 'minify javascripts...'
|
||||
shopt -s extglob
|
||||
for i in ${path_static_js}/!(*[m][i][n].js|*.md)
|
||||
do
|
||||
[[ -e ${i} ]] || break # handle the case of no files found
|
||||
echo ${i}
|
||||
file_no_ext=$(basename "${i%.js}")
|
||||
if ! curl -X POST -s --fail --compressed --data "code_type=js" --data "js_engine=closure" --data-urlencode 'code@'${i} https://htmlcompressor.com/compress > ${path_static_js}/${file_no_ext}.min.js
|
||||
then
|
||||
echo 'ERROR minifying javascript: '${i}
|
||||
shopt -u extglob
|
||||
exit 80
|
||||
fi
|
||||
if [ ! -s "${path_static_js}/${file_no_ext}.min.js" ]; then
|
||||
echo "File is empty"
|
||||
shopt -u extglob
|
||||
exit 81
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
#used to minify css
|
||||
echo 'minify css...'
|
||||
for i in ${path_static_css}/!(*[m][i][n].css|*.md)
|
||||
do
|
||||
[[ -e ${i} ]] || break # handle the case of no files found
|
||||
echo ${i}
|
||||
file_no_ext=$(basename "${i%.css}")
|
||||
if ! curl -X POST -s --fail --compressed --data "code_type=css" --data-urlencode 'code@'${i} https://htmlcompressor.com/compress > ${path_static_css}/${file_no_ext}.min.css
|
||||
then
|
||||
echo 'ERROR minifying css: '${i}
|
||||
shopt -u extglob
|
||||
exit 90
|
||||
fi
|
||||
if [ ! -s "${path_static_css}/${file_no_ext}.min.css" ]; then
|
||||
echo "File is empty"
|
||||
shopt -u extglob
|
||||
exit 91
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
shopt -u extglob
|
||||
echo
|
||||
echo Build ok
|
||||
|
2
static/css/dev/README.md
Normal file
2
static/css/dev/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This folder `static/css/dev/` contains the style scripts for **development** environment.
|
||||
You can change these scripts as you want and rebuild the application
|
138
static/css/dev/style.css
Normal file
138
static/css/dev/style.css
Normal file
@ -0,0 +1,138 @@
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'bootstrap-icons';
|
||||
|
||||
}
|
||||
|
||||
.badge-responsive {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.text-responsive {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.badge-responsive {
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
#collapseFilters.collapsing {
|
||||
position: absolute !important;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
#collapseFilters.collapse.show {
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
max-height: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.img-flag {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px;
|
||||
padding: 3px;
|
||||
background-size: cover !important;
|
||||
max-width: auto;
|
||||
max-height: auto;
|
||||
height: 19px !important;
|
||||
width: 32px !important;
|
||||
-webkit-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
-moz-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
-o-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.ipcs {
|
||||
background-image: url("/static/images/background.webp");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.copyleft {
|
||||
display: inline-block;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
span.search-callsign {
|
||||
background: url(/static/images/search-callsign.svg) no-repeat top left;
|
||||
background-size: contain;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#input-group-callsign {
|
||||
margin-right: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#collapseFilters {
|
||||
margin-top: 10px;
|
||||
background-color: #DDE2E6;
|
||||
}
|
||||
|
||||
#spotsTable {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#band {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#dashboard {
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#telnet-thead {
|
||||
position: sticky;
|
||||
top: 0
|
||||
}
|
||||
|
||||
#form-band_activity {
|
||||
width: 600px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#chart-band_activity {
|
||||
width: 100%;
|
||||
height: 400px
|
||||
}
|
||||
|
||||
#chart-world_dx_spots_live {
|
||||
width: 600px;
|
||||
height: 500px;
|
||||
gap: 0px;
|
||||
}
|
||||
|
||||
#chart-hour_band {
|
||||
width: 600px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#silo-propagation-img {
|
||||
width: 480px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
#chart-dx_spots_x_month {
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
#chart-dx_spots_trend {
|
||||
width: 700px;
|
||||
height: 400px;
|
||||
}
|
2
static/css/rel/README.md
Normal file
2
static/css/rel/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This folder `static/css/rel/` contains the style scripts for **release** environment.
|
||||
You **CANNOT change** these scripts. Make changes only in `static/css/dev/` folder
|
1
static/css/rel/style.min.css
vendored
Normal file
1
static/css/rel/style.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");@font-face{font-display:swap;font-family:bootstrap-icons}.badge-responsive{width:70px}@media screen and (max-width:768px){.text-responsive{font-size:11px}.badge-responsive{width:40px}#collapseFilters.collapsing{position:absolute!important;z-index:20}#collapseFilters.collapse.show{display:block;position:absolute;z-index:20}.navbar-collapse{max-height:none!important}}.img-flag{background-color:#fff;background-size:cover!important;border:1px solid #ddd;border-radius:2px;-webkit-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-moz-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-o-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);height:19px!important;max-height:auto;max-width:auto;padding:3px;width:32px!important}.ipcs{background-image:url(/static/images/background.webp);background-repeat:no-repeat;background-size:cover}.copyleft{display:inline-block;transform:rotate(180deg)}span.search-callsign{background:url(/static/images/search-callsign.svg) no-repeat 0 0;background-size:contain;cursor:pointer;display:inline-block;height:16px;width:20px}#input-group-callsign{margin-bottom:.5rem;margin-right:1rem}#collapseFilters{background-color:#dde2e6;margin-top:10px}#spotsTable{margin-top:10px}#band{margin-top:5px}#dashboard{gap:10px;padding:10px}#telnet-thead{position:sticky;top:0}#form-band_activity{height:500px;width:600px}#chart-band_activity{height:400px;width:100%}#chart-world_dx_spots_live{gap:0;height:500px;width:600px}#chart-hour_band{height:500px;width:600px}#silo-propagation-img{height:400px;width:480px}#chart-dx_spots_x_month{height:400px;width:600px}#chart-dx_spots_trend{height:400px;width:700px}
|
@ -1,128 +0,0 @@
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'bootstrap-icons';
|
||||
|
||||
}
|
||||
|
||||
.badge-responsive {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.text-responsive {
|
||||
font-size: 11px;
|
||||
}
|
||||
.badge-responsive {
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
#collapseFilters.collapsing {
|
||||
position: absolute !important;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
#collapseFilters.collapse.show {
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
max-height: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.img-flag {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px;
|
||||
padding: 3px;
|
||||
background-size:cover !important;
|
||||
max-width: auto;
|
||||
max-height: auto;
|
||||
height: 19px !important;
|
||||
width: 32px !important;
|
||||
-webkit-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
-moz-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
-o-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.ipcs {
|
||||
background-image: url("/static/images/background.webp");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.copyleft {
|
||||
display: inline-block;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
span.search-callsign {
|
||||
background: url(/static/images/search-callsign.svg) no-repeat top left;
|
||||
background-size: contain;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#input-group-callsign {
|
||||
margin-right: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#collapseFilters {
|
||||
margin-top: 10px;
|
||||
background-color: #DDE2E6;
|
||||
}
|
||||
|
||||
#spotsTable {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#band {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#dashboard {
|
||||
padding:10px;
|
||||
gap:10px;
|
||||
}
|
||||
|
||||
#telnet-thead {
|
||||
position: sticky;top: 0
|
||||
}
|
||||
|
||||
#form-band_activity {
|
||||
width: 600px; height: 500px;
|
||||
}
|
||||
|
||||
#chart-band_activity {
|
||||
width:100%; height:400px
|
||||
}
|
||||
|
||||
#chart-world_dx_spots_live {
|
||||
width: 600px; height: 500px; gap: 0px;
|
||||
}
|
||||
|
||||
#chart-hour_band {
|
||||
width: 600px; height: 500px;
|
||||
}
|
||||
|
||||
#silo-propagation-img {
|
||||
width: 480px; height: 400px;
|
||||
}
|
||||
|
||||
#chart-dx_spots_x_month {
|
||||
width: 600px; height: 400px;
|
||||
}
|
||||
|
||||
#chart-dx_spots_trend {
|
||||
width: 700px; height: 400px;
|
||||
}
|
1
static/css/style.min.css
vendored
1
static/css/style.min.css
vendored
@ -1 +0,0 @@
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");@font-face{font-display:swap;font-family:'bootstrap-icons'}.badge-responsive{width:70px}@media screen and (max-width:768px){.text-responsive{font-size:11px}.badge-responsive{width:40px}}@media screen and (max-width:768px){#collapseFilters.collapsing{position:absolute!important;z-index:20}#collapseFilters.collapse.show{display:block;position:absolute;z-index:20}.navbar-collapse{max-height:none!important}}.img-flag{background-color:white;border:1px solid #ddd;border-radius:2px;padding:3px;background-size:cover!important;max-width:auto;max-height:auto;height:19px!important;width:32px!important;-webkit-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-moz-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-o-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5)}.ipcs{background-image:url("/static/images/background.webp");background-size:cover;background-repeat:no-repeat}.copyleft{display:inline-block;transform:rotate(180deg)}span.search-callsign{background:url(/static/images/search-callsign.svg) no-repeat top left;background-size:contain;cursor:pointer;display:inline-block;height:16px;width:20px}#input-group-callsign{margin-right:1rem;margin-bottom:.5rem}#collapseFilters{margin-top:10px;background-color:#dde2e6}#spotsTable{margin-top:10px}#band{margin-top:5px}#dashboard{padding:10px;gap:10px}#telnet-thead{position:sticky;top:0}#form-band_activity{width:600px;height:500px}#chart-band_activity{width:100%;height:400px}#chart-world_dx_spots_live{width:600px;height:500px;gap:0}#chart-hour_band{width:600px;height:500px}#silo-propagation-img{width:480px;height:400px}#chart-dx_spots_x_month{width:600px;height:400px}#chart-dx_spots_trend{width:700px;height:400px}
|
File diff suppressed because it is too large
Load Diff
1
static/html/dev/_base.html
Symbolic link
1
static/html/dev/_base.html
Symbolic link
@ -0,0 +1 @@
|
||||
../../../templates/_base.html
|
1
static/html/dev/offline.html
Symbolic link
1
static/html/dev/offline.html
Symbolic link
@ -0,0 +1 @@
|
||||
../../../templates/offline.html
|
@ -16,7 +16,7 @@
|
||||
<link rel="icon" href="/static/images/icons/spider_ico_master.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/images/icons/icon-apple.png">
|
||||
<link rel="manifest" href="/static/manifest.webmanifest">
|
||||
<link rel="stylesheet" href="/static/css/style.min.css">
|
||||
<link rel="stylesheet" href="/static/css/dev/style.css">
|
||||
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" as="style" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous" onload="this.rel='stylesheet' ">
|
||||
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"></noscript>
|
||||
@ -24,7 +24,7 @@
|
||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css" as="style" integrity="sha512-uvXdJud8WaOlQFjlz9B15Yy2Au/bMAvz79F7Xa6OakCl2jvQPdHD0hb3dEqZRdSwG4/sknePXlE7GiarwA/9Wg==" crossorigin="anonymous" onload="this.rel='stylesheet'" >
|
||||
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css"></noscript>
|
||||
|
||||
<script src="static/js/load_css.min.js"></script>
|
||||
<script src="static/js/dev/load_css.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
@ -47,7 +47,7 @@
|
||||
|
||||
</ul>
|
||||
<div id="MyClockDisplay" onload="showTime()" class="text-white-50 d-none d-lg-block"></div>
|
||||
<div class="text-white-50 d-none d-lg-block" >  (UTC)    </div>
|
||||
<div class="text-white-50 d-none d-lg-block" > (UTC) </div>
|
||||
|
||||
|
||||
</div>
|
||||
@ -75,7 +75,8 @@
|
||||
<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">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>
|
||||
@ -93,40 +94,43 @@
|
||||
<span id="version">v2.4.1</span>
|
||||
</div>
|
||||
</footer>
|
||||
<script async src="static/js/clock.min.js"></script>
|
||||
<script async src="static/js/copy_date.min.js"></script>
|
||||
<script async src="static/js/load-sw.min.js"></script>
|
||||
<script async src="static/js/dev/clock.js"></script>
|
||||
<script async src="static/js/dev/copy_date.js"></script>
|
||||
<script async src="static/js/dev/load-sw.js"></script>
|
||||
<script nonce="sedfGFG32xs">
|
||||
|
||||
|
||||
</script>
|
||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script defer src="static/js/dev/common.js"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
|
||||
|
||||
<script async src="static/js/callsign_search.min.js"></script>
|
||||
<script async src="static/js/dev/callsign_search.js"></script>
|
||||
|
||||
|
||||
|
||||
<!-- cookie consent management -->
|
||||
|
||||
|
||||
<div class="modal" tabindex="-1" id="cookie-consent-container">
|
||||
|
||||
<!-- Modal for cookie consent-->
|
||||
<div class="modal fade" id="cookie_consent_modal" tabindex="-1" aria-labelledby="cookie-consent-container" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title">We use cookies</h1>
|
||||
<h5 class="modal-title" id="exampleModalLabel">We use cookies</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>We use only technical cookies.</p>
|
||||
<p>Clicking "I agree", you agree to the storing of cookies on your device. To learn more about how we use cookies, please see our cookies policy.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="cookie-consent">I agree</button>
|
||||
<button type="button" class="btn btn-primary" id="cookie_consent_btn">I agree</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script defer src="static/js/cookie_consent.min.js"></script>
|
||||
|
||||
<script defer src="static/js/dev/cookie_consent.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
136
static/html/rel/offline.html
Normal file
136
static/html/rel/offline.html
Normal file
@ -0,0 +1,136 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<title>DX Cluster from IU1BOW: OFFLINE</title>
|
||||
<!-- page generated by staticjinja -->
|
||||
|
||||
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="Web Ham Radio DX Cluster and spot search">
|
||||
<meta name="keywords" content="ham radio, dx cluster, dx spots, cluster sposts,web dx cluster,dx cluster search, DX spots">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#2196f3">
|
||||
<link rel="icon" href="/static/images/icons/favicon.ico" size="any">
|
||||
<link rel="icon" href="/static/images/icons/spider_ico_master.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/images/icons/icon-apple.png">
|
||||
<link rel="manifest" href="/static/manifest.webmanifest">
|
||||
<link rel="stylesheet" href="/static/css/rel/style.min.css">
|
||||
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" as="style" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous" onload="this.rel='stylesheet' ">
|
||||
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"></noscript>
|
||||
|
||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css" as="style" integrity="sha512-uvXdJud8WaOlQFjlz9B15Yy2Au/bMAvz79F7Xa6OakCl2jvQPdHD0hb3dEqZRdSwG4/sknePXlE7GiarwA/9Wg==" crossorigin="anonymous" onload="this.rel='stylesheet'" >
|
||||
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css"></noscript>
|
||||
|
||||
<script src="static/js/rel/load_css.min.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
|
||||
|
||||
<!-- nav bar -->
|
||||
<nav class="navbar px-2 navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="/static/images/icons/icon-72x72.png" width="30" height="30" class="d-inline-block align-top" alt="">
|
||||
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" aria-controls="navbarToggler01" aria-expanded="false" aria-label="Toggle navigation" data-bs-toggle="collapse" data-bs-target="#navbarToggler01" >
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarToggler01">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0" >
|
||||
|
||||
</ul>
|
||||
<div id="MyClockDisplay" onload="showTime()" class="text-white-50 d-none d-lg-block"></div>
|
||||
<div class="text-white-50 d-none d-lg-block" > (UTC) </div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
|
||||
</header>
|
||||
<div class="container-fluid mt-1 ml-0 mr-0 px-0">
|
||||
<div class="ipcs bg-light p-4 rounded-lg m-2">
|
||||
|
||||
<h1 class="display-4 text-white">WEB DX Cluster</h1>
|
||||
<p class="lead text-light">Spots list</p>
|
||||
|
||||
<p class="text-light">Telnet access: <a href="telnet://" class="text-white"></a></p>
|
||||
<p class="text-light">For connect your cluster, write to <a href="mailto:?Subject=Connect%20my%20DxCluster%20node" target="_top" class="text-white"></a></p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- <div class="container">
|
||||
<div class="row">
|
||||
<div class="col align-self-center"> -->
|
||||
<div class="jumbotron alert alert-warning" role="alert">
|
||||
<h2 class="display-4">No internet connection</h2>
|
||||
<p class="lead">The features in this area require Internet connectivity. Please connect your computer to the
|
||||
Internet</p>
|
||||
<p class="lead">
|
||||
<a class="btn btn-primary btn-lg" href="/" role="button">Try again</a>
|
||||
</p>
|
||||
<!-- </div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<footer class="page-footer font-small blue">
|
||||
<div class="footer-copyright text-center py-3">
|
||||
<span class="copyleft">©</span> Copyleft:
|
||||
<span id="copyDate"></span>
|
||||
<a href="https://github.com/coulisse/spiderweb/" target="blank" rel="noopener">IU1BOW Spiderweb</a>
|
||||
<span id="version">v2.4.1</span>
|
||||
</div>
|
||||
</footer>
|
||||
<script async src="static/js/rel/clock.min.js"></script>
|
||||
<script async src="static/js/rel/copy_date.min.js"></script>
|
||||
<script async src="static/js/rel/load-sw.min.js"></script>
|
||||
<script nonce="sedfGFG32xs">
|
||||
|
||||
|
||||
</script>
|
||||
<script defer src="static/js/rel/common.min.js"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
|
||||
|
||||
<script async src="static/js/rel/callsign_search.min.js"></script>
|
||||
|
||||
|
||||
|
||||
<!-- cookie consent management -->
|
||||
|
||||
|
||||
|
||||
<!-- Modal for cookie consent-->
|
||||
<div class="modal fade" id="cookie_consent_modal" tabindex="-1" aria-labelledby="cookie-consent-container" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">We use cookies</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>We use only technical cookies.</p>
|
||||
<p>Clicking "I agree", you agree to the storing of cookies on your device. To learn more about how we use cookies, please see our cookies policy.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="cookie_consent_btn">I agree</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script defer src="static/js/rel/cookie_consent.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1 +0,0 @@
|
||||
Make changes only on file without '.min.' in their name, then minify using build.sh in 'script' folder
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* script loaded inline page in order to prepare data
|
||||
* for next operations
|
||||
*/
|
||||
//var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t",""));
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t",""));
|
||||
var rows_list = new Array();
|
||||
|
||||
var qryString = 'spotlist?c='+my_callsign;
|
||||
fetch(qryString)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
try {
|
||||
//rows_list = buildHtmlTable('bodyspot',data, rows_list, my_callsign);
|
||||
tb.build(data,my_callsign);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(err.stack);
|
||||
console.log(data);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
1
static/js/callsign_inline.min.js
vendored
1
static/js/callsign_inline.min.js
vendored
@ -1 +0,0 @@
|
||||
var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t","")),rows_list=[];buildHtmlTable("#bodyspot",payload_json,rows_list,my_callsign);
|
1
static/js/callsign_search.min.js
vendored
1
static/js/callsign_search.min.js
vendored
@ -1 +0,0 @@
|
||||
function myCallsignSearch(){callsign=document.getElementById("callsignInput").value;0<callsign.replace(/\s/g,"").length&&(location.href="/callsign.html?c=".concat(callsign.trim().toUpperCase()))};
|
@ -1,14 +0,0 @@
|
||||
function showTime(){
|
||||
var date=new Date()
|
||||
var utc = new Date(date.getTime() + date.getTimezoneOffset() * 60000);
|
||||
|
||||
var time = utc.toTimeString().split(' ')[0];
|
||||
time = time.split(':')[0]+':'+time.split(':')[1];
|
||||
document.getElementById("MyClockDisplay").innerText = time;
|
||||
document.getElementById("MyClockDisplay").textContent = time;
|
||||
|
||||
setTimeout(showTime, 1000);
|
||||
|
||||
}
|
||||
showTime();
|
||||
|
1
static/js/clock.min.js
vendored
1
static/js/clock.min.js
vendored
@ -1 +0,0 @@
|
||||
function showTime(){var a=new Date;a=(new Date(a.getTime()+6E4*a.getTimezoneOffset())).toTimeString().split(" ")[0];a=a.split(":")[0]+":"+a.split(":")[1];document.getElementById("MyClockDisplay").innerText=a;document.getElementById("MyClockDisplay").textContent=a;setTimeout(showTime,1E3)}showTime();
|
@ -1,167 +0,0 @@
|
||||
/**
|
||||
* Create a cookie
|
||||
*
|
||||
* @param cname {String} cookie name
|
||||
* @param cvalue {string} cookie value
|
||||
* @param exdays {integer} the number of the days for cookie expiration
|
||||
*/
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
|
||||
let expires = "expires=" + d.toUTCString();
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/" + ";Samesite=Strict;Secure=True";
|
||||
};
|
||||
|
||||
/**
|
||||
* get a cookie
|
||||
*
|
||||
* @param cname {String} cookie name
|
||||
* @returns cookie value
|
||||
*/
|
||||
function getCookie(cname) {
|
||||
let name = cname + "=";
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param date {String} date of the last refresh
|
||||
* @returns string formatted
|
||||
*/
|
||||
function get_last_refresh(data) {
|
||||
var dt_refresh = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
||||
var dt_refresh;
|
||||
dt_refresh.setUTCSeconds(data["last_refresh"]);
|
||||
const pad = (n, s = 2) => (`${new Array(s).fill(0)}${n}`).slice(-s);
|
||||
var hours = pad(dt_refresh.getHours());
|
||||
var minutes = pad(dt_refresh.getMinutes());
|
||||
var months_names = get_months_names();
|
||||
var month = months_names[dt_refresh.getMonth()];
|
||||
var day = dt_refresh.getDate();
|
||||
var year = dt_refresh.getFullYear();
|
||||
var last_refresh = "Data refresh: " + day + " of " + month + " " + year + " at " + hours + ":" + minutes;
|
||||
return last_refresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @returns {Array} list of months as short names
|
||||
*/
|
||||
function get_months_names() {
|
||||
var months_name = [];
|
||||
for (let monthNumber = 1; monthNumber < 13; monthNumber++) {
|
||||
const date = new Date();
|
||||
date.setMonth(monthNumber - 1);
|
||||
months_name.push(date.toLocaleString('en-US', {
|
||||
month: 'short'
|
||||
}));
|
||||
};
|
||||
return months_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param {Integer} num Number to be formatted
|
||||
* @returns {string} Number formatted with K for thousands and M for millions
|
||||
*/
|
||||
function format_u_k_m(num) {
|
||||
let label;
|
||||
let sign = 1;
|
||||
if (num < 0) {
|
||||
sign = -1;
|
||||
}
|
||||
let abs_num = Math.abs(num);
|
||||
if (abs_num == 0) {
|
||||
label = abs_num
|
||||
} else {
|
||||
if (abs_num > 0 && abs_num < 1000) {
|
||||
label = abs_num * sign;
|
||||
} else {
|
||||
if (abs_num >= 1000 && abs_num < 1000000) {
|
||||
label = abs_num / 1000 * sign + "K";
|
||||
} else {
|
||||
if (abs_num >= 1000000) {
|
||||
label = abs_num / 1000000 * sign + "M";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a selected element of a combo box with a value
|
||||
*
|
||||
* @param {string} id of the selected
|
||||
* @param {string} valueToSelect value to assign
|
||||
*/
|
||||
function selectElement(id, valueToSelect) {
|
||||
let element = document.getElementById(id);
|
||||
element.value = valueToSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event Handler to element (on)
|
||||
*
|
||||
* @param {string} id of the element
|
||||
* @param {string} eventType i.e. "change"
|
||||
* @param {function} handler the function to execute whene the evenet is raised
|
||||
*/
|
||||
function addEventHandler(id, eventType, handler) {
|
||||
if (id.addEventListener)
|
||||
id.addEventListener(eventType, handler, false);
|
||||
else if (elem.attachEvent)
|
||||
id.attachEvent('on' + eventType, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a text to a specific element
|
||||
*
|
||||
* @param {string} id of the element
|
||||
* @param {string} newvalue the value to assign to element
|
||||
*/
|
||||
function setText(id, newvalue) {
|
||||
var s = document.getElementById(id);
|
||||
s.innerHTML = newvalue;
|
||||
}
|
||||
|
||||
|
||||
function openModal() {
|
||||
document.getElementById("backdrop").style.display = "block"
|
||||
document.getElementById("exampleModal").style.display = "block"
|
||||
document.getElementById("exampleModal").classList.add("show")
|
||||
}
|
||||
function closeModal() {
|
||||
document.getElementById("backdrop").style.display = "none"
|
||||
document.getElementById("exampleModal").style.display = "none"
|
||||
document.getElementById("exampleModal").classList.remove("show")
|
||||
}
|
||||
|
||||
/*
|
||||
function doRefresh(){
|
||||
|
||||
var chartDom = document.getElementById('chart-dx_spots_trend');
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,'/plot_get_dx_spots_trend');
|
||||
|
||||
};
|
||||
|
||||
setInterval(function(){doRefresh()}, 5000);
|
||||
|
||||
*/
|
||||
|
1
static/js/cookie_consent.min.js
vendored
1
static/js/cookie_consent.min.js
vendored
@ -1 +0,0 @@
|
||||
var fn=function(){document.cookie="cookie_consent=true;SameSite=Strict; Secure;max-age=2592000";$("#cookie-consent-container").modal("hide")};document.getElementById("cookie-consent").onclick=fn;$("#cookie-consent-container").modal({backdrop:"static",keyboard:!1});$("#cookie-consent-container").modal("show");
|
@ -17,4 +17,4 @@ rules:
|
||||
- single
|
||||
semi:
|
||||
- error
|
||||
- never
|
||||
- always
|
2
static/js/dev/README.md
Normal file
2
static/js/dev/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This folder `static/js/dev/` contains the javascripts module for **development** environment.
|
||||
You can change these scripts as you want and rebuild the application
|
22
static/js/dev/callsign_inline.js
Normal file
22
static/js/dev/callsign_inline.js
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* script loaded inline page in order to prepare data
|
||||
* for next operations
|
||||
*/
|
||||
//var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t",""));
|
||||
var my_adxo_events = JSON.parse(my_adxo_events_json.replaceAll('\t', ''));
|
||||
|
||||
var qryString = 'spotlist?c=' + my_callsign;
|
||||
fetch(qryString)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
try {
|
||||
tb.build(data, my_callsign);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(err.stack);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
12
static/js/dev/clock.js
Normal file
12
static/js/dev/clock.js
Normal file
@ -0,0 +1,12 @@
|
||||
function showTime(){
|
||||
let date=new Date();
|
||||
let utc = new Date(date.getTime() + date.getTimezoneOffset() * 60000);
|
||||
let time = utc.toTimeString().split(' ')[0];
|
||||
time = time.split(':')[0]+':'+time.split(':')[1];
|
||||
document.getElementById("MyClockDisplay").innerText = time;
|
||||
document.getElementById("MyClockDisplay").textContent = time;
|
||||
setTimeout(showTime, 1000);
|
||||
}
|
||||
|
||||
showTime();
|
||||
|
155
static/js/dev/common.js
Normal file
155
static/js/dev/common.js
Normal file
@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Create a cookie
|
||||
*
|
||||
* @param cname {String} cookie name
|
||||
* @param cvalue {string} cookie value
|
||||
* @param exdays {integer} the number of the days for cookie expiration
|
||||
*/
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
|
||||
let expires = 'expires=' + d.toUTCString();
|
||||
document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/' + ';Samesite=Strict;Secure=True';
|
||||
};
|
||||
|
||||
/**
|
||||
* get a cookie
|
||||
*
|
||||
* @param cname {String} cookie name
|
||||
* @returns cookie value
|
||||
*/
|
||||
function getCookie(cname) {
|
||||
let name = cname + '=';
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param date {String} date of the last refresh
|
||||
* @returns string formatted
|
||||
*/
|
||||
function get_last_refresh(data) {
|
||||
let dt_refresh = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
||||
//var dt_refresh;
|
||||
dt_refresh.setUTCSeconds(data["last_refresh"]);
|
||||
const pad = (n, s = 2) => (`${new Array(s).fill(0)}${n}`).slice(-s);
|
||||
const hours = pad(dt_refresh.getHours());
|
||||
const minutes = pad(dt_refresh.getMinutes());
|
||||
const months_names = get_months_names();
|
||||
const month = months_names[dt_refresh.getMonth()];
|
||||
const day = dt_refresh.getDate();
|
||||
const year = dt_refresh.getFullYear();
|
||||
const last_refresh = "Data refresh: " + day + " of " + month + " " + year + " at " + hours + ":" + minutes;
|
||||
return last_refresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @returns {Array} list of months as short names
|
||||
*/
|
||||
function get_months_names() {
|
||||
var months_name = [];
|
||||
for (let monthNumber = 1; monthNumber < 13; monthNumber++) {
|
||||
const date = new Date();
|
||||
date.setMonth(monthNumber - 1);
|
||||
months_name.push(date.toLocaleString('en-US', {
|
||||
month: 'short'
|
||||
}));
|
||||
}
|
||||
return months_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param {Integer} num Number to be formatted
|
||||
* @returns {string} Number formatted with K for thousands and M for millions
|
||||
*/
|
||||
function format_u_k_m(num) {
|
||||
let label;
|
||||
let sign = 1;
|
||||
if (num < 0) {
|
||||
sign = -1;
|
||||
}
|
||||
let abs_num = Math.abs(num);
|
||||
if (abs_num == 0) {
|
||||
label = abs_num;
|
||||
} else {
|
||||
if (abs_num > 0 && abs_num < 1000) {
|
||||
label = abs_num * sign;
|
||||
} else {
|
||||
if (abs_num >= 1000 && abs_num < 1000000) {
|
||||
label = abs_num / 1000 * sign + 'K';
|
||||
} else {
|
||||
if (abs_num >= 1000000) {
|
||||
label = abs_num / 1000000 * sign + 'M';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a selected element of a combo box with a value
|
||||
*
|
||||
* @param {string} id of the selected
|
||||
* @param {string} valueToSelect value to assign
|
||||
*/
|
||||
function selectElement(id, valueToSelect) {
|
||||
let element = document.getElementById(id);
|
||||
element.value = valueToSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event Handler to element (on)
|
||||
*
|
||||
* @param {string} id of the element
|
||||
* @param {string} eventType i.e. "change"
|
||||
* @param {function} handler the function to execute whene the evenet is raised
|
||||
*/
|
||||
function addEventHandler(id, eventType, handler) {
|
||||
if (id.addEventListener)
|
||||
id.addEventListener(eventType, handler, false);
|
||||
else if (id.attachEvent)
|
||||
id.attachEvent('on' + eventType, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a text to a specific element
|
||||
*
|
||||
* @param {string} id of the element
|
||||
* @param {string} newvalue the value to assign to element
|
||||
*/
|
||||
function setText(id, newvalue) {
|
||||
var s = document.getElementById(id);
|
||||
s.innerHTML = newvalue;
|
||||
}
|
||||
|
||||
/*
|
||||
function doRefresh(){
|
||||
|
||||
var chartDom = document.getElementById('chart-dx_spots_trend');
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,'/plot_get_dx_spots_trend');
|
||||
|
||||
};
|
||||
|
||||
setInterval(function(){doRefresh()}, 5000);
|
||||
|
||||
*/
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
script used to acquire user conset to cookie banner (and set the cookie consent)
|
||||
script used to acquire user consent to cookie banner (and set the cookie consent)
|
||||
*/
|
||||
let cookie_modal = new bootstrap.Modal(document.getElementById('cookie_consent_modal'), {
|
||||
keyboard: false
|
||||
})
|
||||
});
|
||||
cookie_modal.show();
|
||||
|
||||
//if button is pressed, setting cookie
|
@ -1 +1 @@
|
||||
document.getElementById('copyDate').innerHTML='2020-'.concat(new Date().getFullYear())
|
||||
document.getElementById('copyDate').innerHTML='2020-'.concat(new Date().getFullYear());
|
@ -5,8 +5,7 @@
|
||||
* Moreover, add an event to the button of the filter form
|
||||
*/
|
||||
//var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t",""));
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t",""));
|
||||
var rows_list = new Array();
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll('\t',''));
|
||||
|
||||
refresh_timer(); //run first data fetch
|
||||
var myRefresh = setInterval(refresh_timer, timer_interval_json);
|
17
static/js/dev/load-sw.js
Normal file
17
static/js/dev/load-sw.js
Normal file
@ -0,0 +1,17 @@
|
||||
/* this is used to instance service worker for PWA
|
||||
* Check compatibility for the browser we're running this in
|
||||
*/
|
||||
if ('serviceWorker' in navigator) {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
console.log('[PWA Builder] active service worker found, no need to register');
|
||||
} else {
|
||||
// Register the service worker
|
||||
navigator.serviceWorker
|
||||
.register('service-worker.js', {
|
||||
scope: './'
|
||||
})
|
||||
.then(function (reg) {
|
||||
console.log('[PWA Builder] Service worker has been registered for scope: ' + reg.scope);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*! loadCSS. [c]2020 Filament Group, Inc. MIT License */
|
||||
(function(w){
|
||||
"use strict";
|
||||
'use strict';
|
||||
/* exported loadCSS */
|
||||
var loadCSS = function( href, before, media, attributes ){
|
||||
// Arguments explained:
|
||||
@ -10,13 +10,13 @@
|
||||
// `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all'
|
||||
// `attributes` [OPTIONAL] is the Object of attribute name/attribute value pairs to set on the stylesheet's DOM Element.
|
||||
var doc = w.document;
|
||||
var ss = doc.createElement( "link" );
|
||||
var ss = doc.createElement( 'link' );
|
||||
var ref;
|
||||
if( before ){
|
||||
ref = before;
|
||||
}
|
||||
else {
|
||||
var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes;
|
||||
var refs = ( doc.body || doc.getElementsByTagName( 'head' )[ 0 ] ).childNodes;
|
||||
ref = refs[ refs.length - 1];
|
||||
}
|
||||
|
||||
@ -29,10 +29,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
ss.rel = "stylesheet";
|
||||
ss.rel = 'stylesheet';
|
||||
ss.href = href;
|
||||
// temporarily set media to something inapplicable to ensure it'll fetch without blocking render
|
||||
ss.media = "only x";
|
||||
ss.media = 'only x';
|
||||
|
||||
// wait until body is defined before injecting link. This ensures a non-blocking load in IE11.
|
||||
function ready( cb ){
|
||||
@ -65,24 +65,24 @@
|
||||
|
||||
function loadCB(){
|
||||
if( ss.addEventListener ){
|
||||
ss.removeEventListener( "load", loadCB );
|
||||
ss.removeEventListener( 'load', loadCB );
|
||||
}
|
||||
ss.media = media || "all";
|
||||
ss.media = media || 'all';
|
||||
}
|
||||
|
||||
// once loaded, set link's media back to `all` so that the stylesheet applies once it loads
|
||||
if( ss.addEventListener ){
|
||||
ss.addEventListener( "load", loadCB);
|
||||
ss.addEventListener( 'load', loadCB);
|
||||
}
|
||||
ss.onloadcssdefined = onloadcssdefined;
|
||||
onloadcssdefined( loadCB );
|
||||
return ss;
|
||||
};
|
||||
// commonjs
|
||||
if( typeof exports !== "undefined" ){
|
||||
if( typeof exports !== 'undefined' ){
|
||||
exports.loadCSS = loadCSS;
|
||||
}
|
||||
else {
|
||||
w.loadCSS = loadCSS;
|
||||
}
|
||||
}( typeof global !== "undefined" ? global : this ));
|
||||
}( typeof global !== 'undefined' ? global : this ));
|
191
static/js/dev/plot_band_activity.js
Normal file
191
static/js/dev/plot_band_activity.js
Normal file
@ -0,0 +1,191 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build band_activity chart
|
||||
* ******************************************************************************/
|
||||
class band_activity {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {string} region This is the continent name (EU,AF,NA...) of the selected
|
||||
* @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF)
|
||||
* @param {Json} continents The continent list( EU, SA, ...)
|
||||
*/
|
||||
refresh(my_chart, end_point, region, bands, continents){
|
||||
// Asynchronous Data Loading
|
||||
fetch(end_point+'?continent='+region)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=Array.from(data['band activity']).map(function (item) {
|
||||
return [item[1], item[0], item[2] || '-'];
|
||||
});
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
position: 'top',
|
||||
formatter: function (p) {
|
||||
var format = p.seriesName +' on ' + p.name +' band: '+'<strong>'+p.data[2]+'</strong>';
|
||||
return format;
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text:'Band activity',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
right: 'right',
|
||||
top : 'bottom',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
height: '80%',
|
||||
left: 25,
|
||||
top: 50,
|
||||
right: 60,
|
||||
bottom: 0,
|
||||
show: true,
|
||||
backgroundColor: 'rgb(255, 255, 255)',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: bands,
|
||||
axisTick: {
|
||||
show: true,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: continents,
|
||||
axisTick: {
|
||||
show: true,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
calculable: true,
|
||||
orient: 'vertical',
|
||||
right: 'right',
|
||||
top: 'center',
|
||||
min: 0,
|
||||
max: 30,
|
||||
inRange : {
|
||||
color: ['#ffffe6','yellow','red']
|
||||
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Spots',
|
||||
type: 'heatmap',
|
||||
data: dataMap,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(100, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} cont_cq The continent list( EU, SA, ...)
|
||||
* @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
constructor(chart_id, end_point, cont_cq, band_freq) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
|
||||
//populate continents array
|
||||
var continents=[];
|
||||
cont_cq.forEach(function myFunction(item, index) {
|
||||
continents[index]=item['id'];
|
||||
});
|
||||
|
||||
//populate bands array
|
||||
var bands=[];
|
||||
band_freq.forEach(function myFunction(item, index) {
|
||||
bands[index]=item['id'];
|
||||
});
|
||||
|
||||
//managing region
|
||||
var selectedContinent=getCookie('user_region');
|
||||
var selectedContinent_desc=getCookie('user_region_desc');
|
||||
if (!selectedContinent) {
|
||||
selectedContinent='EU';
|
||||
selectedContinent_desc='Europe';
|
||||
setCookie('user_region',selectedContinent,60);
|
||||
setCookie('user_region_desc',selectedContinent_desc,60);
|
||||
};
|
||||
|
||||
selectElement('continentInput', selectedContinent);
|
||||
|
||||
addEventHandler(document.getElementById('continentInput'), 'change', function() {
|
||||
selectedContinent=this.value;
|
||||
selectedContinent_desc=this.options[this.selectedIndex].text;
|
||||
setCookie('user_region',selectedContinent,60);
|
||||
setCookie('user_region_desc',selectedContinent_desc,60);
|
||||
plot_ba.refresh(myChart, end_point, selectedContinent,bands,continents);
|
||||
setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band');
|
||||
});
|
||||
|
||||
setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band');
|
||||
|
||||
this.refresh(myChart, end_point, selectedContinent,bands,continents);
|
||||
|
||||
/* resize chart*/
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_ba = new band_activity ('chart-band_activity','/plot_get_heatmap_data',continents_cq,band_frequencies);
|
||||
|
||||
/*setInterval(function(){doRefresh('chart-band_activity','/plot_get_heatmap_data')}, 5000);
|
||||
|
||||
function doRefresh(chart_id,end_point){
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,end_point);
|
||||
};
|
||||
|
||||
*/
|
||||
|
142
static/js/dev/plot_dx_spots_per_month.js
Normal file
142
static/js/dev/plot_dx_spots_per_month.js
Normal file
@ -0,0 +1,142 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build dx spots per month chart
|
||||
* ******************************************************************************/
|
||||
|
||||
class dx_spots_per_month {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
//$.getJSON(end_point).done(function(data) {
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var year_now = new Date().getFullYear();
|
||||
var year_0 = (year_now - 0).toString();
|
||||
var year_1 = (year_now - 1).toString();
|
||||
var year_2 = (year_now - 2).toString();
|
||||
|
||||
var months_name = get_months_names();
|
||||
|
||||
var dataMap_year_0=[];
|
||||
var dataMap_year_1=[];
|
||||
var dataMap_year_2=[];
|
||||
for (let i = 1; i < 13; i++) {
|
||||
dataMap_year_0.push(data.spots_per_month[i].year_0);
|
||||
dataMap_year_1.push(data.spots_per_month[i].year_1);
|
||||
dataMap_year_2.push(data.spots_per_month[i].year_2);
|
||||
}
|
||||
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: 'DX SPOTS per month',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
legend: {
|
||||
data: [year_2, year_1, year_0],
|
||||
bottom: 'bottom'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar', 'stack'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: months_name
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (function (value){
|
||||
return format_u_k_m(value);
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: year_2,
|
||||
type: 'bar',
|
||||
barGap: 0,
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_2
|
||||
},
|
||||
{
|
||||
name: year_1,
|
||||
type: 'bar',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_1
|
||||
|
||||
},
|
||||
{
|
||||
name: year_0,
|
||||
type: 'bar',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_0
|
||||
},
|
||||
]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_dspm = new dx_spots_per_month ('chart-dx_spots_x_month','/plot_get_dx_spots_per_month');
|
136
static/js/dev/plot_dx_spots_trend.js
Normal file
136
static/js/dev/plot_dx_spots_trend.js
Normal file
@ -0,0 +1,136 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build dx spots trend
|
||||
* ******************************************************************************/
|
||||
|
||||
class dx_spots_trend {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
//$.getJSON(end_point).done(function(data) {
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=[];
|
||||
for (const [key, value] of Object.entries(data['spots_trend'])) {
|
||||
var tuple=[];
|
||||
tuple.push(key);
|
||||
tuple.push(value);
|
||||
dataMap.push(tuple);
|
||||
}
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
position: function (pt) {
|
||||
return [pt[0], '10%'];
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: 'DX SPOTS trend',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
saveAsImage: {},
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '10%'],
|
||||
axisLabel: {
|
||||
formatter: (function (value){
|
||||
return format_u_k_m(value);
|
||||
})
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 65,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 20
|
||||
},
|
||||
],
|
||||
|
||||
series: [
|
||||
{
|
||||
name: 'Spots',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
itemStyle: {
|
||||
color: '#078513'
|
||||
},
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#57fa75'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#118226'
|
||||
}
|
||||
])
|
||||
},
|
||||
data: dataMap
|
||||
}
|
||||
]
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_dst = new dx_spots_trend ('chart-dx_spots_trend','/plot_get_dx_spots_trend');
|
152
static/js/dev/plot_hour_band.js
Normal file
152
static/js/dev/plot_hour_band.js
Normal file
@ -0,0 +1,152 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build band/hour chart
|
||||
* ******************************************************************************/
|
||||
|
||||
class hour_band {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
refresh(my_chart,end_point,bands) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the dat
|
||||
var last_refresh=get_last_refresh(data);
|
||||
//set hour indicator names
|
||||
var hour_indicators=[];
|
||||
|
||||
//for (let i = 0; i < 24; i++) {
|
||||
for (let i = 23; i > -1; i--) {
|
||||
var hour_name={};
|
||||
var s=i.toString();
|
||||
hour_name['name']=s;
|
||||
hour_indicators.push(hour_name);
|
||||
}
|
||||
|
||||
//cycling whithin each bands and hours
|
||||
var dataMap=[];
|
||||
bands.forEach(band_item => {
|
||||
var qso=[];
|
||||
|
||||
//for (let i = 0; i < 24; i++) {
|
||||
for (let i = 23; i > -1; i--) {
|
||||
try {
|
||||
var value=data['hour_band'][band_item][i];
|
||||
if (typeof value == 'undefined') {
|
||||
value = 0;
|
||||
}
|
||||
qso.push(value);
|
||||
} catch (TypeError) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
var tot={'value':qso,'name':band_item};
|
||||
dataMap.push(tot);
|
||||
});
|
||||
|
||||
|
||||
//options
|
||||
my_chart.setOption({
|
||||
legend: {
|
||||
|
||||
orient: 'horizontal',
|
||||
left: 'left',
|
||||
bottom: 'bottom'
|
||||
},
|
||||
title: {
|
||||
text: 'DX SPOTS per hour in last month',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
right: 'right',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
radar: {
|
||||
shape: 'circle',
|
||||
//startAngle: 285, //0 on left side
|
||||
startAngle: 105, //0 on top
|
||||
indicator: hour_indicators,
|
||||
center: ['47%', '46%'],
|
||||
axisName: {
|
||||
color: 'rgb(80,80,80)',
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
type: 'radar',
|
||||
symbol: 'none',
|
||||
data: dataMap,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params) => {
|
||||
return 'Band: '+params.name;
|
||||
},
|
||||
},
|
||||
emphasis: {
|
||||
lineStyle: {
|
||||
width: 4
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
constructor(chart_id,end_point,band_freq) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
|
||||
//populate bands array
|
||||
var bands=[];
|
||||
band_freq.forEach(function myFunction(item, index) {
|
||||
bands[index]=item['id']
|
||||
});
|
||||
|
||||
this.refresh(myChart,end_point,bands);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_hb = new hour_band('chart-hour_band','/plot_get_hour_band',band_frequencies);
|
161
static/js/dev/plot_world_dx_spots_live.js
Normal file
161
static/js/dev/plot_world_dx_spots_live.js
Normal file
@ -0,0 +1,161 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build world dx spots live
|
||||
* ******************************************************************************/
|
||||
|
||||
class world_dx_spots_live {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
fetch('world.json')
|
||||
.then(response => response.text())
|
||||
.then(geoJson => {
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=[];
|
||||
data['world_dx_spots_live'].forEach(function myFunction(item, index) {
|
||||
//lon, lat, number of qso
|
||||
dataMap.push({'value':[item['lat'],item['lon'],item['count']]});
|
||||
});
|
||||
|
||||
my_chart.hideLoading();
|
||||
echarts.registerMap('WR', geoJson);
|
||||
|
||||
my_chart.setOption( {
|
||||
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 50,
|
||||
inRange: {
|
||||
symbolSize: [5, 20]
|
||||
}
|
||||
},
|
||||
|
||||
geo: {
|
||||
type: 'map',
|
||||
map: 'WR',
|
||||
roam: true,
|
||||
zoom: 1.2,
|
||||
aspectScale: 0.70,
|
||||
layoutCenter: ['50%', '54%'],
|
||||
layoutSize: '100%',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#323c48',
|
||||
borderColor: '#111'
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#2a333d'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function(val) {
|
||||
var out='Spots: <STRONG>'+ val.value[2] +'</STRONG>';
|
||||
return out;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
iconStyle: {
|
||||
borderColor: '#fff',
|
||||
},
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
//backgroundColor: '#404a59',
|
||||
backgroundColor: '#596475',
|
||||
title: {
|
||||
text: 'World DX SPOTS in last hour',
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
right:'right',
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
subtextStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
data:dataMap,
|
||||
label: {
|
||||
emphasis: {
|
||||
position: 'right',
|
||||
show: false
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#eea638'
|
||||
}
|
||||
},
|
||||
/*
|
||||
symbolSize: function (val) {
|
||||
return val[2] / 4;
|
||||
},
|
||||
*/
|
||||
}
|
||||
]
|
||||
|
||||
}); //end options
|
||||
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector('#'+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_wdsl = new world_dx_spots_live ('chart-world_dx_spots_live','/plot_get_world_dx_spots_live');
|
360
static/js/dev/table.js
Normal file
360
static/js/dev/table.js
Normal file
@ -0,0 +1,360 @@
|
||||
class table_builder {
|
||||
/**
|
||||
* Table builder constructor
|
||||
* @param selector {string} The html identifier where build the spots table
|
||||
*/
|
||||
constructor(selector) {
|
||||
this.selector = selector;
|
||||
this.current_data = [];
|
||||
}
|
||||
/**
|
||||
* @return last_rowid {integer} the last rowid
|
||||
*/
|
||||
getLastRowId() {
|
||||
let last_rowid;
|
||||
if (this.current_data == null) {
|
||||
last_rowid = 0;
|
||||
} else {
|
||||
if (this.current_data.length < 1) {
|
||||
last_rowid = 0;
|
||||
} else {
|
||||
last_rowid = this.current_data[0].rowid;
|
||||
}
|
||||
}
|
||||
return last_rowid;
|
||||
}
|
||||
|
||||
resetData() {
|
||||
this.current_data = [];
|
||||
}
|
||||
/**
|
||||
* @param line {object} with the data of a single spot
|
||||
* @param isnew {boolean} is the new rows indicator
|
||||
* @param dt_current {string} current date in dd/mm/yyyy format
|
||||
* @param callsign {string} optional callsign
|
||||
* @return a <tr> dom element
|
||||
*/
|
||||
#buildRow(line, isnew, dt_current, callsign = '') {
|
||||
const row = document.createElement('tr');
|
||||
row.id = line.rowid;
|
||||
|
||||
if (callsign.length > 0) {
|
||||
if (callsign == line.de) {
|
||||
row.id = line.rowid;
|
||||
} else if (callsign == line.dx) {
|
||||
row.id = line.rowid;
|
||||
}
|
||||
} else if (isnew) {
|
||||
row.className = 'table-info';
|
||||
}
|
||||
|
||||
//Column: DE search on QRZ
|
||||
const i_qrzde = document.createElement('i');
|
||||
i_qrzde.className = 'bi-search';
|
||||
i_qrzde.role = 'button';
|
||||
i_qrzde.ariaLabel = line.de;
|
||||
const a_qrzde = document.createElement('a');
|
||||
a_qrzde.href = qrz_url + line.de;
|
||||
a_qrzde.target = '_blank';
|
||||
a_qrzde.rel = 'noopener';
|
||||
const span_qrzde = document.createElement('span');
|
||||
|
||||
//Mark DE if it found in callsign search
|
||||
if (line.de == callsign) {
|
||||
const mark_qrzde = document.createElement('mark');
|
||||
mark_qrzde.textContent = line.de;
|
||||
span_qrzde.appendChild(mark_qrzde);
|
||||
} else {
|
||||
span_qrzde.textContent = '\xa0' + line.de;
|
||||
}
|
||||
|
||||
const td_qrzde = document.createElement('td');
|
||||
|
||||
a_qrzde.appendChild(i_qrzde);
|
||||
td_qrzde.appendChild(a_qrzde);
|
||||
td_qrzde.appendChild(span_qrzde);
|
||||
row.append(td_qrzde);
|
||||
|
||||
//Column: frequency
|
||||
var freq = Intl.NumberFormat('it-IT', {
|
||||
style: 'decimal'
|
||||
}).format(line.freq);
|
||||
|
||||
const span_freq = document.createElement('span');
|
||||
span_freq.className = 'badge bg-warning text-dark badge-responsive';
|
||||
span_freq.textContent = freq;
|
||||
|
||||
const td_freq = document.createElement('td');
|
||||
td_freq.appendChild(span_freq);
|
||||
|
||||
row.appendChild(td_freq);
|
||||
|
||||
//Column: DX (with ADXO Management)
|
||||
var adxo = findAdxo(my_adxo_events, line.dx);
|
||||
var adxo_link = '<a href=' + adxo_url + ' target=_blank rel=noopener >NG3K Website</a>';
|
||||
const i_qrzdx = document.createElement('i');
|
||||
i_qrzdx.className = 'bi-search';
|
||||
i_qrzdx.role = 'button';
|
||||
i_qrzdx.ariaLabel = line.dx;
|
||||
const a_qrzdx = document.createElement('a');
|
||||
a_qrzdx.href = qrz_url + line.dx;
|
||||
a_qrzdx.target = '_blank';
|
||||
a_qrzdx.rel = 'noopener';
|
||||
const span_qrzdx = document.createElement('span');
|
||||
|
||||
//Mark DX if it found in callsign search
|
||||
const mark_qrzdx = document.createElement('mark');
|
||||
mark_qrzdx.textContent = line.dx;
|
||||
if (line.dx == callsign) {
|
||||
span_qrzdx.appendChild(mark_qrzdx);
|
||||
} else {
|
||||
span_qrzdx.textContent = '\xa0' + line.dx;
|
||||
}
|
||||
|
||||
if (adxo != undefined) {
|
||||
const i_adxo = document.createElement('i');
|
||||
i_adxo.tabIndex = 0;
|
||||
i_adxo.className = 'bi-megaphone-fill';
|
||||
i_adxo.style = 'color: cornflowerblue;';
|
||||
i_adxo.role = 'button';
|
||||
i_adxo.ariaLabel = 'dx_operations';
|
||||
i_adxo.setAttribute('data-bs-container', 'body');
|
||||
i_adxo.setAttribute('data-bs-toggle', 'popover');
|
||||
i_adxo.setAttribute('data-bs-trigger', 'focus');
|
||||
i_adxo.setAttribute('data-bs-sanitizer', 'true');
|
||||
i_adxo.setAttribute('data-bs-placement', 'auto');
|
||||
i_adxo.setAttribute('data-bs-html', 'true');
|
||||
i_adxo.setAttribute('data-bs-title', 'Announced DX Op.: ' + adxo.summary);
|
||||
i_adxo.setAttribute('data-bs-content', adxo.description + 'data from ' + adxo_link);
|
||||
span_qrzdx.appendChild(i_adxo);
|
||||
}
|
||||
|
||||
const td_qrzdx = document.createElement('td');
|
||||
a_qrzdx.appendChild(i_qrzdx);
|
||||
td_qrzdx.appendChild(a_qrzdx);
|
||||
td_qrzdx.append(span_qrzdx);
|
||||
row.appendChild(td_qrzdx);
|
||||
|
||||
//Column: Flag
|
||||
try {
|
||||
const span_flag = document.createElement('span');
|
||||
span_flag.className = 'img-flag fi fi-' + line.iso;
|
||||
span_flag.setAttribute('data-bs-container', 'body');
|
||||
span_flag.setAttribute('data-bs-toggle', 'popover');
|
||||
span_flag.setAttribute('data-bs-trigger', 'hover');
|
||||
span_flag.setAttribute('data-bs-placement', 'left');
|
||||
span_flag.setAttribute('data-bs-content', line.country);
|
||||
|
||||
const td_flag = document.createElement('td');
|
||||
td_flag.appendChild(span_flag);
|
||||
row.appendChild(td_flag);
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log('error creating flag');
|
||||
const td_flag = document.createElement('td');
|
||||
row.appendChild(td_flag);
|
||||
}
|
||||
|
||||
//Column: Country
|
||||
const td_country_code = document.createElement('td');
|
||||
td_country_code.className = 'd-none d-lg-table-cell d-xl-table-cell';
|
||||
td_country_code.textContent = line.country;
|
||||
row.appendChild(td_country_code);
|
||||
|
||||
//Column: Comment
|
||||
const td_comm = document.createElement('td');
|
||||
td_comm.className = 'd-none d-lg-table-cell d-xl-table-cell';
|
||||
td_comm.textContent = line.comm;
|
||||
row.appendChild(td_comm);
|
||||
|
||||
//Column: UTC
|
||||
let dt = new Date(line.time * 1000);
|
||||
let hh = '00' + dt.getUTCHours();
|
||||
hh = hh.substring(hh.length - 2, hh.length);
|
||||
let mi = '00' + dt.getMinutes();
|
||||
mi = mi.substring(mi.length - 2, mi.length);
|
||||
let dd = '00' + dt.getUTCDate();
|
||||
dd = dd.substring(dd.length - 2, dd.length);
|
||||
let mo = '00' + (Number(dt.getUTCMonth()) + 1);
|
||||
mo = mo.substring(mo.length - 2, mo.length);
|
||||
let yy = dt.getUTCFullYear();
|
||||
let tm = hh + ':' + mi;
|
||||
dt = dd + '/' + mo + '/' + yy;
|
||||
|
||||
const div_date_time = document.createElement('div');
|
||||
div_date_time.className = 'd-flex flex-column';
|
||||
const p_time = document.createElement('div');
|
||||
p_time.textContent = tm;
|
||||
div_date_time.appendChild(p_time);
|
||||
if (dt != dt_current) {
|
||||
const p_date = document.createElement('div');
|
||||
p_date.textContent = dt;
|
||||
div_date_time.appendChild(p_date);
|
||||
}
|
||||
|
||||
row.appendChild(div_date_time);
|
||||
|
||||
//Finally append the row created to the table
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the table with the spot
|
||||
*
|
||||
* @param data {json} The payload with all the spots received from cluster
|
||||
* @param rl {json} Row List
|
||||
* @param callsign {string} An optional parameter with the callsign to search
|
||||
*/
|
||||
build(data, callsign) {
|
||||
|
||||
if (data != null) {
|
||||
|
||||
//get current date
|
||||
let d = new Date();
|
||||
let dd_current = '00' + d.getUTCDate();
|
||||
dd_current = dd_current.substring(dd_current.length - 2, dd_current.length);
|
||||
let mo_current = '00' + (Number(d.getUTCMonth()) + 1);
|
||||
mo_current = mo_current.substring(mo_current.length - 2, mo_current.length);
|
||||
let yy_current = d.getUTCFullYear();
|
||||
let dt_current = dd_current + '/' + mo_current + '/' + yy_current;
|
||||
|
||||
//empty the table
|
||||
document.getElementById(this.selector).replaceChildren();
|
||||
|
||||
//insert in table new elements
|
||||
let merge_data = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
document.getElementById(this.selector).append(this.#buildRow(data[i], true, dt_current, callsign));
|
||||
merge_data.push(data[i]);
|
||||
}
|
||||
|
||||
//insert in html table previous elements
|
||||
for (let i = 0; i < this.current_data.length - data.length; i++) {
|
||||
document.getElementById(this.selector).append(this.#buildRow(this.current_data[i], false, dt_current, callsign));
|
||||
merge_data.push(this.current_data[i]);
|
||||
}
|
||||
|
||||
//replace current data with merge
|
||||
this.current_data = merge_data;
|
||||
|
||||
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
|
||||
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
|
||||
return new bootstrap.Popover(popoverTriggerEl);
|
||||
});
|
||||
}
|
||||
}
|
||||
} //end class
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
* javascript used to popolate main table with spots
|
||||
* ******************************************************************************/
|
||||
const adxo_url = 'https://www.ng3k.com/misc/adxo.html';
|
||||
const qrz_url = 'https://www.qrz.com/db/';
|
||||
const tb = new table_builder('bodyspot');
|
||||
var qryAll_sv = '';
|
||||
|
||||
/**
|
||||
* Decode Announced Dx Operation (ng3k)
|
||||
*
|
||||
* @param adxo {adxo} This is the json containing all the dxo events
|
||||
* @param callsign_to_find {callsign_to_find} The callsign of the current dx line
|
||||
*/
|
||||
function findAdxo(adxo, callsign_to_find) {
|
||||
if (adxo) {
|
||||
for (let i = 0; i < adxo.length; i++) {
|
||||
if (adxo[i].callsign == callsign_to_find) {
|
||||
return adxo[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Function to filter spot when pressed the search button on filter
|
||||
* This function trigger the search, also triggered by timer
|
||||
*/
|
||||
function mySearch(event) {
|
||||
event.preventDefault();
|
||||
refresh_timer(); //force the call of query
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for construct query string for single value selection
|
||||
*
|
||||
* @param id {string} The html identifier used for filter
|
||||
* @param param {string}the parameter for the query
|
||||
* @param len {number} The maximum number of element that could be selected; use -1 if the filter permits a single selection
|
||||
* @param qrystr {string} Th initial query string to be completed with the new filter
|
||||
*/
|
||||
function getFilter(id, param, len, qrystr) {
|
||||
|
||||
let selectedFilter = [].map.call(document.getElementById(id).selectedOptions, option => option.value);
|
||||
let qryFilter = '';
|
||||
if (selectedFilter.length < len || len == -1) {
|
||||
qryFilter = selectedFilter.map(function (el) {
|
||||
if (el) {
|
||||
return param + '=' + el;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}).join('&');
|
||||
qrystr = qrystr.concat('&'.concat(qryFilter));
|
||||
if (qrystr.substring(0, 1) == '&') {
|
||||
qrystr = qrystr.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
return qrystr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search / Filter cluster spot based on filter settings
|
||||
* Gets the filter values, constructs the query parameter and
|
||||
* make the request to the server
|
||||
*/
|
||||
function refresh_timer() {
|
||||
|
||||
let qryAll = '';
|
||||
|
||||
//get other filters
|
||||
qryAll = getFilter('band', 'b', 14, qryAll);
|
||||
qryAll = getFilter('de_re', 'e', 7, qryAll);
|
||||
qryAll = getFilter('dx_re', 'x', 7, qryAll);
|
||||
qryAll = getFilter('mode', 'm', 3, qryAll);
|
||||
qryAll = getFilter('cqdeInput', 'qe', -1, qryAll);
|
||||
qryAll = getFilter('cqdxInput', 'qx', -1, qryAll);
|
||||
|
||||
//Composing query string
|
||||
let qryString;
|
||||
|
||||
//If the filter is changed we need to reset the data table and restart from rowid=0
|
||||
if (qryAll != qryAll_sv) {
|
||||
tb.resetData();
|
||||
qryAll_sv = qryAll;
|
||||
}
|
||||
|
||||
qryString = ('spotlist?lr=').concat(tb.getLastRowId());
|
||||
|
||||
if (qryAll) {
|
||||
qryString = qryString.concat('&'.concat(qryAll));
|
||||
}
|
||||
|
||||
|
||||
console.log(qryString);
|
||||
|
||||
//Open a new connection, using the GET request on the URL endpoint
|
||||
fetch(qryString)
|
||||
.then((response) => response.json())
|
||||
.then((data_new) => {
|
||||
try {
|
||||
tb.build(data_new);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(err.stack);
|
||||
console.log(data_new);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
1
static/js/index_inline.min.js
vendored
1
static/js/index_inline.min.js
vendored
@ -1 +0,0 @@
|
||||
var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t","")),rows_list=[];buildHtmlTable("#bodyspot",payload_json,rows_list);var myRefresh=setInterval(myTimer,timer_interval_json);window.onload=function(){document.getElementById("form-filters").addEventListener("submit",mySearch)};
|
@ -1,17 +0,0 @@
|
||||
/* this is used to instance service worker for PWA
|
||||
* Check compatibility for the browser we're running this in
|
||||
*/
|
||||
if ("serviceWorker" in navigator) {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
console.log("[PWA Builder] active service worker found, no need to register");
|
||||
} else {
|
||||
// Register the service worker
|
||||
navigator.serviceWorker
|
||||
.register("service-worker.js", {
|
||||
scope: "./"
|
||||
})
|
||||
.then(function (reg) {
|
||||
console.log("[PWA Builder] Service worker has been registered for scope: " + reg.scope);
|
||||
});
|
||||
}
|
||||
}
|
4
static/js/load_css.min.js
vendored
4
static/js/load_css.min.js
vendored
@ -1,4 +0,0 @@
|
||||
/*
|
||||
loadCSS. [c]2020 Filament Group, Inc. MIT License */
|
||||
(function(l){var e=function(e,f,q,d){function m(a){if(b.body)return a();setTimeout(function(){m(a)})}function g(){a.addEventListener&&a.removeEventListener("load",g);a.media=q||"all"}var b=l.document,a=b.createElement("link");if(f)var c=f;else{var n=(b.body||b.getElementsByTagName("head")[0]).childNodes;c=n[n.length-1]}var p=b.styleSheets;if(d)for(var h in d)d.hasOwnProperty(h)&&a.setAttribute(h,d[h]);a.rel="stylesheet";a.href=e;a.media="only x";m(function(){c.parentNode.insertBefore(a,f?c:c.nextSibling)});
|
||||
var k=function(b){for(var d=a.href,c=p.length;c--;)if(p[c].href===d)return b();setTimeout(function(){k(b)})};a.addEventListener&&a.addEventListener("load",g);a.onloadcssdefined=k;k(g);return a};"undefined"!==typeof exports?exports.loadCSS=e:l.loadCSS=e})("undefined"!==typeof global?global:this);
|
@ -1,191 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build band_activity chart
|
||||
* ******************************************************************************/
|
||||
class band_activity {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {string} region This is the continent name (EU,AF,NA...) of the selected
|
||||
* @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF)
|
||||
* @param {Json} continents The continent list( EU, SA, ...)
|
||||
*/
|
||||
refresh(my_chart, end_point, region, bands, continents){
|
||||
// Asynchronous Data Loading
|
||||
fetch(end_point+'?continent='+region)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=Array.from(data["band activity"]).map(function (item) {
|
||||
return [item[1], item[0], item[2] || '-'];
|
||||
});
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
position: 'top',
|
||||
formatter: function (p) {
|
||||
var format = p.seriesName +' on ' + p.name +' band: '+'<strong>'+p.data[2]+'</strong>';
|
||||
return format;
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text:"Band activity",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
right: 'right',
|
||||
top : 'bottom',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
height: '80%',
|
||||
left: 25,
|
||||
top: 50,
|
||||
right: 60,
|
||||
bottom: 0,
|
||||
show: true,
|
||||
backgroundColor: 'rgb(255, 255, 255)',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: bands,
|
||||
axisTick: {
|
||||
show: true,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: continents,
|
||||
axisTick: {
|
||||
show: true,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
calculable: true,
|
||||
orient: 'vertical',
|
||||
right: 'right',
|
||||
top: 'center',
|
||||
min: 0,
|
||||
max: 30,
|
||||
inRange : {
|
||||
color: ['#ffffe6','yellow','red']
|
||||
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Spots',
|
||||
type: 'heatmap',
|
||||
data: dataMap,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(100, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} cont_cq The continent list( EU, SA, ...)
|
||||
* @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
constructor(chart_id, end_point, cont_cq, band_freq) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
|
||||
//populate continents array
|
||||
var continents=[];
|
||||
cont_cq.forEach(function myFunction(item, index) {
|
||||
continents[index]=item['id']
|
||||
});
|
||||
|
||||
//populate bands array
|
||||
var bands=[];
|
||||
band_freq.forEach(function myFunction(item, index) {
|
||||
bands[index]=item['id']
|
||||
});
|
||||
|
||||
//managing region
|
||||
var selectedContinent=getCookie("user_region");
|
||||
var selectedContinent_desc=getCookie("user_region_desc");
|
||||
if (!selectedContinent) {
|
||||
selectedContinent="EU";
|
||||
selectedContinent_desc="Europe";
|
||||
setCookie("user_region",selectedContinent,60);
|
||||
setCookie("user_region_desc",selectedContinent_desc,60);
|
||||
};
|
||||
|
||||
selectElement('continentInput', selectedContinent);
|
||||
|
||||
addEventHandler(document.getElementById('continentInput'), 'change', function() {
|
||||
selectedContinent=this.value;
|
||||
selectedContinent_desc=this.options[this.selectedIndex].text;
|
||||
setCookie("user_region",selectedContinent,60);
|
||||
setCookie("user_region_desc",selectedContinent_desc,60);
|
||||
plot_ba.refresh(myChart, end_point, selectedContinent,bands,continents);
|
||||
setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band');
|
||||
});
|
||||
|
||||
setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band');
|
||||
|
||||
this.refresh(myChart, end_point, selectedContinent,bands,continents);
|
||||
|
||||
/* resize chart*/
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_ba = new band_activity ('chart-band_activity','/plot_get_heatmap_data',continents_cq,band_frequencies);
|
||||
|
||||
/*setInterval(function(){doRefresh('chart-band_activity','/plot_get_heatmap_data')}, 5000);
|
||||
|
||||
function doRefresh(chart_id,end_point){
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,end_point);
|
||||
};
|
||||
|
||||
*/
|
||||
|
10
static/js/plot_band_activity.min.js
vendored
10
static/js/plot_band_activity.min.js
vendored
@ -1,10 +0,0 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,c,b){a!=Array.prototype&&a!=Object.prototype&&(a[c]=b.value)};
|
||||
$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var c=0;c<a.length;++c){var b=a[c];if(b&&b.Math==Math)return b}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.polyfill=function(a,c,b,d){if(c){b=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in b||(b[e]={});b=b[e]}a=a[a.length-1];d=b[a];c=c(d);c!=d&&null!=c&&$jscomp.defineProperty(b,a,{configurable:!0,writable:!0,value:c})}};
|
||||
$jscomp.polyfill("Array.from",function(a){return a?a:function(a,b,d){b=null!=b?b:function(a){return a};var c=[],f="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];if("function"==typeof f){a=f.call(a);for(var g=0;!(f=a.next()).done;)c.push(b.call(d,f.value,g++))}else for(f=a.length,g=0;g<f;g++)c.push(b.call(d,a[g],g));return c}},"es6","es3");
|
||||
$jscomp.findInternal=function(a,c,b){a instanceof String&&(a=String(a));for(var d=a.length,e=0;e<d;e++){var f=a[e];if(c.call(b,f,e,a))return{i:e,v:f}}return{i:-1,v:void 0}};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,b){return $jscomp.findInternal(this,a,b).v}},"es6","es3");
|
||||
var band_activity=function(a,c,b,d){var e=document.getElementById(a),f=echarts.init(e),g=[];b.forEach(function(a,b){g[b]=a.id});var l=[];d.forEach(function(a,b){l[b]=a.id});var h=getCookie("user_region"),k=getCookie("user_region_desc");h||(h="EU",k="Europe",setCookie("user_region",h,60),setCookie("user_region_desc",k,60));$("select").val(h).change();$("select").on("change",function(){h=this.value;k=$(this).find("option:selected").text();setCookie("user_region",h,60);setCookie("user_region_desc",k,
|
||||
60);plot_ba.refresh(f,c,h,l,g);$("#txt_continent").text(" Based on DX SPOTS from stations in "+k+" during the last 15 minutes, displayed by Continent and Band")});$("#txt_continent").text(" Based on DX SPOTS from stations in "+k+" during the last 15 minutes, displayed by Continent and Band");this.refresh(f,c,h,l,g);var m=echarts.init(document.querySelector("#"+a),null);window.addEventListener("resize",function(){m.resize()})};
|
||||
band_activity.prototype.refresh=function(a,c,b,d,e){fetch(c+"?continent="+b).then(function(a){return a.json()}).then(function(b){var c=get_last_refresh(b);b=Array.from(b["band activity"]).map(function(a){return[a[1],a[0],a[2]||"-"]});a.setOption({tooltip:{position:"top",formatter:function(a){return a.seriesName+" on "+a.name+" band: <strong>"+a.data[2]+"</strong>"}},title:{text:"Band activity",subtext:c,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,orient:"vertical",right:"right",top:"bottom",
|
||||
feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},grid:{height:"80%",left:25,top:50,right:60,bottom:0,show:!0,backgroundColor:"rgb(255, 255, 255)"},xAxis:{type:"category",data:d,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},yAxis:{type:"category",data:e,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},visualMap:{calculable:!0,orient:"vertical",right:"right",top:"center",min:0,max:30,inRange:{color:["#ffffe6","yellow","red"]}},series:[{name:"Spots",
|
||||
type:"heatmap",data:b,label:{show:!1},emphasis:{itemStyle:{shadowBlur:10,shadowColor:"rgba(100, 0, 0, 0.5)"}}}]})})};var plot_ba=new band_activity("chart-band_activity","/plot_get_heatmap_data",continents_cq,band_frequencies);
|
@ -1,119 +0,0 @@
|
||||
|
||||
/**
|
||||
* Create a cookie
|
||||
*
|
||||
* @param {String} cname cookie name
|
||||
* @param {string} cvalue cookie value
|
||||
* @param {integer} exdays the number of the days for cookie expiration
|
||||
*/
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (exdays*24*60*60*1000));
|
||||
let expires = "expires="+ d.toUTCString();
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/" + ";Samesite=Strict;Secure=True";
|
||||
};
|
||||
|
||||
/**
|
||||
* get a cookie
|
||||
*
|
||||
* @param {String} cname cookie name
|
||||
* @returns {string} cookie value
|
||||
*/
|
||||
function getCookie(cname) {
|
||||
let name = cname + "=";
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
for(let i = 0; i <ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param {String} date date of the last refresh
|
||||
* @returns {string} string formatted
|
||||
*/
|
||||
function get_last_refresh(data){
|
||||
var dt_refresh = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
||||
var dt_refresh;
|
||||
dt_refresh.setUTCSeconds(data["last_refresh"]);
|
||||
const pad = (n,s=2) => (`${new Array(s).fill(0)}${n}`).slice(-s);
|
||||
var hours = pad (dt_refresh.getHours());
|
||||
var minutes = pad(dt_refresh.getMinutes());
|
||||
var months_names = get_months_names();
|
||||
var month = months_names[dt_refresh.getMonth()];
|
||||
var day = dt_refresh.getDate();
|
||||
var year = dt_refresh.getFullYear();
|
||||
var last_refresh = "Data refresh: "+day+" of "+month+" "+year+" at "+hours+":"+minutes;
|
||||
return last_refresh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @returns {Array} list of months as short names
|
||||
*/
|
||||
function get_months_names() {
|
||||
var months_name=[];
|
||||
for (let monthNumber = 1; monthNumber < 13; monthNumber++) {
|
||||
const date = new Date();
|
||||
date.setMonth(monthNumber - 1);
|
||||
months_name.push(date.toLocaleString('en-US', { month: 'short' }));
|
||||
};
|
||||
return months_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* format the data refresh string for exhibit on charts
|
||||
*
|
||||
* @param {Integer} num Number to be formatted
|
||||
* @returns {string} Number formatted with K for thousands and M for millions
|
||||
*/
|
||||
function format_u_k_m(num) {
|
||||
let label;
|
||||
let sign=1;
|
||||
if (num<0){
|
||||
sign=-1;
|
||||
}
|
||||
let abs_num=Math.abs(num);
|
||||
if (abs_num==0) {
|
||||
label=abs_num
|
||||
} else {
|
||||
if (abs_num>0 && abs_num <1000) {
|
||||
label=abs_num*sign;
|
||||
} else {
|
||||
if (abs_num>=1000 && abs_num < 1000000) {
|
||||
label=abs_num/1000*sign+"K";
|
||||
} else {
|
||||
if (abs_num>=1000000) {
|
||||
label=abs_num/1000000*sign+"M";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
/*
|
||||
function doRefresh(){
|
||||
|
||||
var chartDom = document.getElementById('chart-dx_spots_trend');
|
||||
var myChart = echarts.init(chartDom);
|
||||
plot_dst.refresh(myChart,'/plot_get_dx_spots_trend');
|
||||
|
||||
};
|
||||
|
||||
setInterval(function(){doRefresh()}, 5000);
|
||||
|
||||
*/
|
6
static/js/plot_common.min.js
vendored
6
static/js/plot_common.min.js
vendored
@ -1,6 +0,0 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.createTemplateTagFirstArg=function(a){return a.raw=a};$jscomp.createTemplateTagFirstArgWithRaw=function(a,b){a.raw=b;return a};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};
|
||||
$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};$jscomp.polyfill("Array.prototype.fill",function(a){return a?a:function(a,c,d){var b=this.length||0;0>c&&(c=Math.max(0,b+c));if(null==d||d>b)d=b;d=Number(d);0>d&&(d=Math.max(0,b+d));for(c=Number(c||0);c<d;c++)this[c]=a;return this}},"es6","es3");
|
||||
function setCookie(a,b,c){var d=new Date;d.setTime(d.getTime()+864E5*c);c="expires="+d.toUTCString();document.cookie=a+"="+b+";"+c+";path=/;Samesite=Strict;Secure=True"}function getCookie(a){a+="=";for(var b=decodeURIComponent(document.cookie).split(";"),c=0;c<b.length;c++){for(var d=b[c];" "==d.charAt(0);)d=d.substring(1);if(0==d.indexOf(a))return d.substring(a.length,d.length)}return""}
|
||||
function get_last_refresh(a){var b=new Date(0);b.setUTCSeconds(a.last_refresh);var c=function(a,b){b=void 0===b?2:b;return(""+Array(b).fill(0)+a).slice(-b)};a=c(b.getHours());c=c(b.getMinutes());var d=get_months_names()[b.getMonth()],e=b.getDate();b=b.getFullYear();return"Data refresh: "+e+" of "+d+" "+b+" at "+a+":"+c}function get_months_names(){for(var a=[],b=1;13>b;b++){var c=new Date;c.setMonth(b-1);a.push(c.toLocaleString("en-US",{month:"short"}))}return a}
|
||||
function format_u_k_m(a){var b,c=1;0>a&&(c=-1);a=Math.abs(a);0==a?b=a:0<a&&1E3>a?b=a*c:1E3<=a&&1E6>a?b=a/1E3*c+"K":1E6<=a&&(b=a/1E6*c+"M");return b};
|
@ -1,142 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build dx spots per month chart
|
||||
* ******************************************************************************/
|
||||
|
||||
class dx_spots_per_month {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
//$.getJSON(end_point).done(function(data) {
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var year_now = new Date().getFullYear();
|
||||
var year_0 = (year_now - 0).toString();
|
||||
var year_1 = (year_now - 1).toString();
|
||||
var year_2 = (year_now - 2).toString();
|
||||
|
||||
var months_name = get_months_names();
|
||||
|
||||
var dataMap_year_0=[];
|
||||
var dataMap_year_1=[];
|
||||
var dataMap_year_2=[];
|
||||
for (let i = 1; i < 13; i++) {
|
||||
dataMap_year_0.push(data.spots_per_month[i].year_0);
|
||||
dataMap_year_1.push(data.spots_per_month[i].year_1);
|
||||
dataMap_year_2.push(data.spots_per_month[i].year_2);
|
||||
}
|
||||
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: "DX SPOTS per month",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
legend: {
|
||||
data: [year_2, year_1, year_0],
|
||||
bottom: "bottom"
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar', 'stack'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: months_name
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (function (value){
|
||||
return format_u_k_m(value)
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: year_2,
|
||||
type: 'bar',
|
||||
barGap: 0,
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_2
|
||||
},
|
||||
{
|
||||
name: year_1,
|
||||
type: 'bar',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_1
|
||||
|
||||
},
|
||||
{
|
||||
name: year_0,
|
||||
type: 'bar',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: dataMap_year_0
|
||||
},
|
||||
]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_dspm = new dx_spots_per_month ('chart-dx_spots_x_month','/plot_get_dx_spots_per_month');
|
4
static/js/plot_dx_spots_per_month.min.js
vendored
4
static/js/plot_dx_spots_per_month.min.js
vendored
@ -1,4 +0,0 @@
|
||||
var dx_spots_per_month=function(e,c){var a=document.getElementById(e);a=echarts.init(a);this.refresh(a,c);var m=echarts.init(document.querySelector("#"+e),null);window.addEventListener("resize",function(){m.resize()})};
|
||||
dx_spots_per_month.prototype.refresh=function(e,c){fetch(c).then(function(a){return a.json()}).then(function(a){var c=get_last_refresh(a),b=(new Date).getFullYear(),f=(b-0).toString(),g=(b-1).toString();b=(b-2).toString();for(var n=get_months_names(),h=[],k=[],l=[],d=1;13>d;d++)h.push(a.spots_per_month[d].year_0),k.push(a.spots_per_month[d].year_1),l.push(a.spots_per_month[d].year_2);e.setOption({tooltip:{trigger:"axis",axisPointer:{type:"shadow"}},title:{text:"DX SPOTS per month",subtext:c,top:"top",
|
||||
left:"left"},legend:{data:[b,g,f],bottom:"bottom"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},magicType:{show:!0,type:["line","bar","stack"]},restore:{show:!0},saveAsImage:{show:!0}}},xAxis:[{type:"category",axisTick:{show:!1},data:n}],yAxis:[{type:"value",axisLabel:{formatter:function(a){return format_u_k_m(a)}}}],series:[{name:b,type:"bar",barGap:0,emphasis:{focus:"series"},data:l},{name:g,type:"bar",emphasis:{focus:"series"},
|
||||
data:k},{name:f,type:"bar",emphasis:{focus:"series"},data:h}]})})};var plot_dspm=new dx_spots_per_month("chart-dx_spots_x_month","/plot_get_dx_spots_per_month");
|
@ -1,136 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build dx spots trend
|
||||
* ******************************************************************************/
|
||||
|
||||
class dx_spots_trend {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
//$.getJSON(end_point).done(function(data) {
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the data
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=[];
|
||||
for (const [key, value] of Object.entries(data["spots_trend"])) {
|
||||
var tuple=[];
|
||||
tuple.push(key);
|
||||
tuple.push(value);
|
||||
dataMap.push(tuple);
|
||||
}
|
||||
//options
|
||||
my_chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
position: function (pt) {
|
||||
return [pt[0], '10%'];
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: "DX SPOTS trend",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
left:'left'
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
saveAsImage: {},
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '10%'],
|
||||
axisLabel: {
|
||||
formatter: (function (value){
|
||||
return format_u_k_m(value)
|
||||
})
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 65,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 20
|
||||
},
|
||||
],
|
||||
|
||||
series: [
|
||||
{
|
||||
name: 'Spots',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
itemStyle: {
|
||||
color: '#078513'
|
||||
},
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#57fa75'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#118226'
|
||||
}
|
||||
])
|
||||
},
|
||||
data: dataMap
|
||||
}
|
||||
]
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_dst = new dx_spots_trend ('chart-dx_spots_trend','/plot_get_dx_spots_trend');
|
8
static/js/plot_dx_spots_trend.min.js
vendored
8
static/js/plot_dx_spots_trend.min.js
vendored
@ -1,8 +0,0 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(a){var b=0;return function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}}};$jscomp.arrayIterator=function(a){return{next:$jscomp.arrayIteratorImpl(a)}};$jscomp.makeIterator=function(a){var b="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];return b?b.call(a):$jscomp.arrayIterator(a)};$jscomp.owns=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};$jscomp.ASSUME_ES5=!1;
|
||||
$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};
|
||||
$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};$jscomp.polyfill("Object.entries",function(a){return a?a:function(a){var c=[],b;for(b in a)$jscomp.owns(a,b)&&c.push([b,a[b]]);return c}},"es8","es3");
|
||||
var dx_spots_trend=function(a,b){var c=document.getElementById(a);c=echarts.init(c);this.refresh(c,b);var d=echarts.init(document.querySelector("#"+a),null);window.addEventListener("resize",function(){d.resize()})};
|
||||
dx_spots_trend.prototype.refresh=function(a,b){fetch(b).then(function(a){return a.json()}).then(function(b){var c=get_last_refresh(b),e=[];b=$jscomp.makeIterator(Object.entries(b.spots_trend));for(var f=b.next();!f.done;f=b.next()){var g=$jscomp.makeIterator(f.value);f=g.next().value;g=g.next().value;var h=[];h.push(f);h.push(g);e.push(h)}a.setOption({tooltip:{trigger:"axis",position:function(a){return[a[0],"10%"]}},title:{text:"DX SPOTS trend",subtext:c,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,
|
||||
orient:"vertical",left:"right",top:"center",feature:{dataView:{show:!0,readOnly:!1},dataZoom:{yAxisIndex:"none"},restore:{},magicType:{show:!0,type:["line","bar"]},saveAsImage:{}}},xAxis:{type:"time",boundaryGap:!1},yAxis:{type:"value",boundaryGap:[0,"10%"],axisLabel:{formatter:function(a){return format_u_k_m(a)}}},dataZoom:[{type:"inside",start:65,end:100},{start:0,end:20}],series:[{name:"Spots",type:"line",smooth:!0,symbol:"none",itemStyle:{color:"#078513"},areaStyle:{color:new echarts.graphic.LinearGradient(0,
|
||||
0,0,1,[{offset:0,color:"#57fa75"},{offset:1,color:"#118226"}])},data:e}]})})};var plot_dst=new dx_spots_trend("chart-dx_spots_trend","/plot_get_dx_spots_trend");
|
@ -1,152 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build band/hour chart
|
||||
* ******************************************************************************/
|
||||
|
||||
class hour_band {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
refresh(my_chart,end_point,bands) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Fill in the dat
|
||||
var last_refresh=get_last_refresh(data);
|
||||
//set hour indicator names
|
||||
var hour_indicators=[];
|
||||
|
||||
//for (let i = 0; i < 24; i++) {
|
||||
for (let i = 23; i > -1; i--) {
|
||||
var hour_name={};
|
||||
var s=i.toString();
|
||||
hour_name["name"]=s;
|
||||
hour_indicators.push(hour_name);
|
||||
}
|
||||
|
||||
//cycling whithin each bands and hours
|
||||
var dataMap=[];
|
||||
bands.forEach(band_item => {
|
||||
var qso=[];
|
||||
|
||||
//for (let i = 0; i < 24; i++) {
|
||||
for (let i = 23; i > -1; i--) {
|
||||
try {
|
||||
var value=data["hour_band"][band_item][i];
|
||||
if (typeof value == 'undefined') {
|
||||
value = 0;
|
||||
}
|
||||
qso.push(value);
|
||||
} catch (TypeError) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
var tot={"value":qso,"name":band_item};
|
||||
dataMap.push(tot);
|
||||
});
|
||||
|
||||
|
||||
//options
|
||||
my_chart.setOption({
|
||||
legend: {
|
||||
|
||||
orient: 'horizontal',
|
||||
left: 'left',
|
||||
bottom: 'bottom'
|
||||
},
|
||||
title: {
|
||||
text: "DX SPOTS per hour in last month",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
right: 'right',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
radar: {
|
||||
shape: 'circle',
|
||||
//startAngle: 285, //0 on left side
|
||||
startAngle: 105, //0 on top
|
||||
indicator: hour_indicators,
|
||||
center: ['47%', '46%'],
|
||||
axisName: {
|
||||
color: 'rgb(80,80,80)',
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
type: 'radar',
|
||||
symbol: 'none',
|
||||
data: dataMap,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params) => {
|
||||
return "Band: "+params.name;
|
||||
},
|
||||
},
|
||||
emphasis: {
|
||||
lineStyle: {
|
||||
width: 4
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
* @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF)
|
||||
*/
|
||||
constructor(chart_id,end_point,band_freq) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
|
||||
//populate bands array
|
||||
var bands=[];
|
||||
band_freq.forEach(function myFunction(item, index) {
|
||||
bands[index]=item['id']
|
||||
});
|
||||
|
||||
this.refresh(myChart,end_point,bands);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_hb = new hour_band('chart-hour_band','/plot_get_hour_band',band_frequencies);
|
4
static/js/plot_hour_band.min.js
vendored
4
static/js/plot_hour_band.min.js
vendored
@ -1,4 +0,0 @@
|
||||
var hour_band=function(c,a,f){var b=document.getElementById(c);b=echarts.init(b);var d=[];f.forEach(function(b,c){d[c]=b.id});this.refresh(b,a,d);var e=echarts.init(document.querySelector("#"+c),null);window.addEventListener("resize",function(){e.resize()})};
|
||||
hour_band.prototype.refresh=function(c,a,f){fetch(a).then(function(b){return b.json()}).then(function(b){for(var d=get_last_refresh(b),e=[],a=23;-1<a;a--){var g={};g.name=a.toString();e.push(g)}var h=[];f.forEach(function(a){for(var c=[],d=23;-1<d;d--)try{var e=b.hour_band[a][d];"undefined"==typeof e&&(e=0);c.push(e)}catch(k){}h.push({value:c,name:a})});c.setOption({legend:{orient:"horizontal",left:"left",bottom:"bottom"},title:{text:"DX SPOTS per hour in last month",subtext:d,top:"top",right:"right"},
|
||||
tooltip:{trigger:"axis"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},radar:{shape:"circle",startAngle:105,indicator:e,center:["47%","46%"],axisName:{color:"rgb(80,80,80)"}},series:[{lineStyle:{width:2},type:"radar",symbol:"none",data:h,tooltip:{trigger:"item",formatter:function(a){return"Band: "+a.name}},emphasis:{lineStyle:{width:4}}}]})})};
|
||||
var plot_hb=new hour_band("chart-hour_band","/plot_get_hour_band",band_frequencies);
|
@ -1,163 +0,0 @@
|
||||
/********************************************************************************
|
||||
* javascript used to build world dx spots live
|
||||
* ******************************************************************************/
|
||||
|
||||
class world_dx_spots_live {
|
||||
|
||||
/**
|
||||
* refresh and populate chart
|
||||
*
|
||||
* @param {String} my_cart The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
refresh(my_chart,end_point) {
|
||||
|
||||
// Asynchronous Data Loading
|
||||
fetch(end_point)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
fetch('world.json')
|
||||
.then(response => response.text())
|
||||
.then(geoJson => {
|
||||
var last_refresh=get_last_refresh(data);
|
||||
var dataMap=[];
|
||||
data["world_dx_spots_live"].forEach(function myFunction(item, index) {
|
||||
//lon, lat, number of qso
|
||||
dataMap.push({"value":[item["lat"],item["lon"],item["count"]]});
|
||||
});
|
||||
|
||||
my_chart.hideLoading();
|
||||
echarts.registerMap('WR', geoJson);
|
||||
|
||||
my_chart.setOption( {
|
||||
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 50,
|
||||
inRange: {
|
||||
symbolSize: [5, 20]
|
||||
}
|
||||
},
|
||||
|
||||
geo: {
|
||||
type: 'map',
|
||||
map: 'WR',
|
||||
roam: true,
|
||||
zoom: 1.2,
|
||||
aspectScale: 0.70,
|
||||
layoutCenter: ['50%', '54%'],
|
||||
layoutSize: '100%',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#323c48',
|
||||
borderColor: '#111'
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#2a333d'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function(val) {
|
||||
var out="lat: "+val.value[0]+
|
||||
" lon: "+val.value[1]+"</BR>"+
|
||||
"Spots: <STRONG>"+ val.value[2] +"</STRONG></BR>";
|
||||
return out;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
toolbox: {
|
||||
show: true,
|
||||
showTitle: false,
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
top: 'center',
|
||||
iconStyle: {
|
||||
borderColor: '#fff',
|
||||
},
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
//backgroundColor: '#404a59',
|
||||
backgroundColor: '#596475',
|
||||
title: {
|
||||
text: "World DX SPOTS in last hour",
|
||||
subtext: last_refresh,
|
||||
top: 'top',
|
||||
right:'right',
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
subtextStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
data:dataMap,
|
||||
label: {
|
||||
emphasis: {
|
||||
position: 'right',
|
||||
show: false
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#eea638'
|
||||
}
|
||||
},
|
||||
/*
|
||||
symbolSize: function (val) {
|
||||
return val[2] / 4;
|
||||
},
|
||||
*/
|
||||
}
|
||||
]
|
||||
|
||||
}) //end options
|
||||
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart creator
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} chart_id The HTML id where place chart
|
||||
* @param {string} end_point This is backend end-point for chart datas
|
||||
*/
|
||||
constructor(chart_id,end_point) {
|
||||
// Initialize the echarts instance based on the prepared dom
|
||||
var chartDom = document.getElementById(chart_id);
|
||||
var myChart = echarts.init(chartDom);
|
||||
this.refresh(myChart,end_point);
|
||||
|
||||
//resize
|
||||
var chart = echarts.init(document.querySelector("#"+chart_id), null);
|
||||
window.addEventListener('resize',function(){
|
||||
chart.resize();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//create object
|
||||
let plot_wdsl = new world_dx_spots_live ('chart-world_dx_spots_live','/plot_get_world_dx_spots_live');
|
4
static/js/plot_world_dx_spots_live.min.js
vendored
4
static/js/plot_world_dx_spots_live.min.js
vendored
@ -1,4 +0,0 @@
|
||||
var world_dx_spots_live=function(b,c){var a=document.getElementById(b);a=echarts.init(a);this.refresh(a,c);var d=echarts.init(document.querySelector("#"+b),null);window.addEventListener("resize",function(){d.resize()})};
|
||||
world_dx_spots_live.prototype.refresh=function(b,c){fetch(c).then(function(a){return a.json()}).then(function(a){fetch("world.json").then(function(a){return a.text()}).then(function(c){var d=get_last_refresh(a),e=[];a.world_dx_spots_live.forEach(function(a,b){e.push({value:[a.lat,a.lon,a.count]})});b.hideLoading();echarts.registerMap("WR",c);b.setOption({visualMap:{show:!1,min:0,max:50,inRange:{symbolSize:[5,20]}},geo:{type:"map",map:"WR",roam:!0,zoom:1.2,aspectScale:.7,layoutCenter:["50%","54%"],
|
||||
layoutSize:"100%",itemStyle:{normal:{areaColor:"#323c48",borderColor:"#111"},emphasis:{areaColor:"#2a333d"}},label:{emphasis:{show:!1}}},tooltip:{trigger:"item",formatter:function(a){return"lat: "+a.value[0]+" lon: "+a.value[1]+"</BR>Spots: <STRONG>"+a.value[2]+"</STRONG></BR>"}},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",iconStyle:{borderColor:"#fff"},feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},restore:{show:!0},saveAsImage:{show:!0}}},legend:{show:!1},backgroundColor:"#596475",
|
||||
title:{text:"World DX SPOTS in last hour",subtext:d,top:"top",right:"right",textStyle:{color:"#fff"},subtextStyle:{color:"#fff"}},series:[{type:"scatter",coordinateSystem:"geo",data:e,label:{emphasis:{position:"right",show:!1}},itemStyle:{normal:{color:"#eea638"}}}]})})})};var plot_wdsl=new world_dx_spots_live("chart-world_dx_spots_live","/plot_get_world_dx_spots_live");
|
2
static/js/rel/README.md
Normal file
2
static/js/rel/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This folder `static/js/rel/` contains the javascript modules for **release** environment.
|
||||
You **CANNOT change** these scripts. Make changes only in `static/js/dev/` folder
|
1
static/js/rel/callsign_inline.min.js
vendored
Normal file
1
static/js/rel/callsign_inline.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t","")),qryString="spotlist?c="+my_callsign;fetch(qryString).then(l=>l.json()).then(o=>{try{tb.build(o,my_callsign)}catch(l){console.log(l),console.log(l.stack),console.log(o)}});
|
1
static/js/rel/callsign_search.min.js
vendored
Normal file
1
static/js/rel/callsign_search.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
function myCallsignSearch(){0<(callsign=document.getElementById("callsignInput").value).replace(/\s/g,"").length&&(location.href="/callsign.html?c=".concat(callsign.trim().toUpperCase()))}
|
1
static/js/rel/clock.min.js
vendored
Normal file
1
static/js/rel/clock.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
function showTime(){var e=new Date;let t=new Date(e.getTime()+6e4*e.getTimezoneOffset()).toTimeString().split(" ")[0];t=t.split(":")[0]+":"+t.split(":")[1],document.getElementById("MyClockDisplay").innerText=t,document.getElementById("MyClockDisplay").textContent=t,setTimeout(showTime,1e3)}showTime();
|
1
static/js/rel/common.min.js
vendored
Normal file
1
static/js/rel/common.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
function setCookie(e,t,n){var r=new Date,n=(r.setTime(r.getTime()+24*n*60*60*1e3),"expires="+r.toUTCString());document.cookie=e+"="+t+";"+n+";path=/;Samesite=Strict;Secure=True"}function getCookie(e){var n=e+"=",r=decodeURIComponent(document.cookie).split(";");for(let t=0;t<r.length;t++){let e=r[t];for(;" "==e.charAt(0);)e=e.substring(1);if(0==e.indexOf(n))return e.substring(n.length,e.length)}return""}function get_last_refresh(e){var t=new Date(0),e=(t.setUTCSeconds(e.last_refresh),(e,t=2)=>(""+new Array(t).fill(0)+e).slice(-t)),n=e(t.getHours()),e=e(t.getMinutes()),r=get_months_names()[t.getMonth()];return"Data refresh: "+t.getDate()+" of "+r+" "+t.getFullYear()+" at "+n+":"+e}function get_months_names(){var t=[];for(let e=1;e<13;e++){var n=new Date;n.setMonth(e-1),t.push(n.toLocaleString("en-US",{month:"short"}))}return t}function format_u_k_m(e){let t,n=1;e<0&&(n=-1);e=Math.abs(e);return 0==e?t=e:0<e&&e<1e3?t=e*n:1e3<=e&&e<1e6?t=e/1e3*n+"K":1e6<=e&&(t=e/1e6*n+"M"),t}function selectElement(e,t){document.getElementById(e).value=t}function addEventHandler(e,t,n){e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent&&e.attachEvent("on"+t,n)}function setText(e,t){document.getElementById(e).innerHTML=t}
|
1
static/js/rel/cookie_consent.min.js
vendored
Normal file
1
static/js/rel/cookie_consent.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
let cookie_modal=new bootstrap.Modal(document.getElementById("cookie_consent_modal"),{keyboard:!1});cookie_modal.show(),document.getElementById("cookie_consent_btn").onclick=function(){setCookie("cookie_consent",!0,30),cookie_modal.hide()};
|
1
static/js/rel/index_inline.min.js
vendored
Normal file
1
static/js/rel/index_inline.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t","")),myRefresh=(refresh_timer(),setInterval(refresh_timer,timer_interval_json));window.onload=()=>{document.getElementById("form-filters").addEventListener("submit",mySearch)};
|
@ -1 +1 @@
|
||||
"serviceWorker"in navigator&&(navigator.serviceWorker.controller?console.log("[PWA Builder] active service worker found, no need to register"):navigator.serviceWorker.register("service-worker.js",{scope:"./"}).then(function(a){console.log("[PWA Builder] Service worker has been registered for scope: "+a.scope)}));
|
||||
"serviceWorker"in navigator&&(navigator.serviceWorker.controller?console.log("[PWA Builder] active service worker found, no need to register"):navigator.serviceWorker.register("service-worker.js",{scope:"./"}).then(function(e){console.log("[PWA Builder] Service worker has been registered for scope: "+e.scope)}));
|
1
static/js/rel/load_css.min.js
vendored
Normal file
1
static/js/rel/load_css.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(c){"use strict";function e(e,t,n,o){var i,r,d=c.document,a=d.createElement("link"),f=(r=t||(i=(d.body||d.getElementsByTagName("head")[0]).childNodes)[i.length-1],d.styleSheets);if(o)for(var l in o)o.hasOwnProperty(l)&&a.setAttribute(l,o[l]);function s(e){for(var t=a.href,n=f.length;n--;)if(f[n].href===t)return e();setTimeout(function(){s(e)})}function u(){a.addEventListener&&a.removeEventListener("load",u),a.media=n||"all"}return a.rel="stylesheet",a.href=e,a.media="only x",function e(t){if(d.body)return t();setTimeout(function(){e(t)})}(function(){r.parentNode.insertBefore(a,t?r:r.nextSibling)}),a.addEventListener&&a.addEventListener("load",u),a.onloadcssdefined=s,s(u),a}"undefined"!=typeof exports?exports.loadCSS=e:c.loadCSS=e}("undefined"!=typeof global?global:this);
|
1
static/js/rel/plot_band_activity.min.js
vendored
Normal file
1
static/js/rel/plot_band_activity.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class band_activity{refresh(n,t,e,o,i){fetch(t+"?continent="+e).then(t=>t.json()).then(t=>{var e=get_last_refresh(t),t=Array.from(t["band activity"]).map(function(t){return[t[1],t[0],t[2]||"-"]});n.setOption({tooltip:{position:"top",formatter:function(t){return t.seriesName+" on "+t.name+" band: <strong>"+t.data[2]+"</strong>"}},title:{text:"Band activity",subtext:e,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,orient:"vertical",right:"right",top:"bottom",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},grid:{height:"80%",left:25,top:50,right:60,bottom:0,show:!0,backgroundColor:"rgb(255, 255, 255)"},xAxis:{type:"category",data:o,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},yAxis:{type:"category",data:i,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},visualMap:{calculable:!0,orient:"vertical",right:"right",top:"center",min:0,max:30,inRange:{color:["#ffffe6","yellow","red"]}},series:[{name:"Spots",type:"heatmap",data:t,label:{show:!1},emphasis:{itemStyle:{shadowBlur:10,shadowColor:"rgba(100, 0, 0, 0.5)"}}}]})})}constructor(t,e,n,o){var i=document.getElementById(t),s=echarts.init(i),a=[],r=(n.forEach(function(t,e){a[e]=t.id}),[]),c=(o.forEach(function(t,e){r[e]=t.id}),getCookie("user_region")),d=getCookie("user_region_desc"),h=(c||(c="EU",d="Europe",setCookie("user_region",c,60),setCookie("user_region_desc",d,60)),selectElement("continentInput",c),addEventHandler(document.getElementById("continentInput"),"change",function(){c=this.value,d=this.options[this.selectedIndex].text,setCookie("user_region",c,60),setCookie("user_region_desc",d,60),plot_ba.refresh(s,e,c,r,a),setText("txt_continent"," Based on DX SPOTS from stations in "+d+" during the last 15 minutes, displayed by Continent and Band")}),setText("txt_continent"," Based on DX SPOTS from stations in "+d+" during the last 15 minutes, displayed by Continent and Band"),this.refresh(s,e,c,r,a),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){h.resize()})}}let plot_ba=new band_activity("chart-band_activity","/plot_get_heatmap_data",continents_cq,band_frequencies);
|
1
static/js/rel/plot_dx_spots_per_month.min.js
vendored
Normal file
1
static/js/rel/plot_dx_spots_per_month.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class dx_spots_per_month{refresh(h,t){fetch(t).then(t=>t.json()).then(e=>{var t=get_last_refresh(e),s=(new Date).getFullYear(),o=(+s).toString(),r=(s-1).toString(),s=(s-2).toString(),a=get_months_names(),n=[],i=[],p=[];for(let t=1;t<13;t++)n.push(e.spots_per_month[t].year_0),i.push(e.spots_per_month[t].year_1),p.push(e.spots_per_month[t].year_2);h.setOption({tooltip:{trigger:"axis",axisPointer:{type:"shadow"}},title:{text:"DX SPOTS per month",subtext:t,top:"top",left:"left"},legend:{data:[s,r,o],bottom:"bottom"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},magicType:{show:!0,type:["line","bar","stack"]},restore:{show:!0},saveAsImage:{show:!0}}},xAxis:[{type:"category",axisTick:{show:!1},data:a}],yAxis:[{type:"value",axisLabel:{formatter:function(t){return format_u_k_m(t)}}}],series:[{name:s,type:"bar",barGap:0,emphasis:{focus:"series"},data:p},{name:r,type:"bar",emphasis:{focus:"series"},data:i},{name:o,type:"bar",emphasis:{focus:"series"},data:n}]})})}constructor(t,e){var s=document.getElementById(t),s=echarts.init(s),o=(this.refresh(s,e),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){o.resize()})}}let plot_dspm=new dx_spots_per_month("chart-dx_spots_x_month","/plot_get_dx_spots_per_month");
|
1
static/js/rel/plot_dx_spots_trend.min.js
vendored
Normal file
1
static/js/rel/plot_dx_spots_trend.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class dx_spots_trend{refresh(a,t){fetch(t).then(t=>t.json()).then(t=>{var e,o,r=get_last_refresh(t),s=[];for([e,o]of Object.entries(t.spots_trend)){var n=[];n.push(e),n.push(o),s.push(n)}a.setOption({tooltip:{trigger:"axis",position:function(t){return[t[0],"10%"]}},title:{text:"DX SPOTS trend",subtext:r,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{dataView:{show:!0,readOnly:!1},dataZoom:{yAxisIndex:"none"},restore:{},magicType:{show:!0,type:["line","bar"]},saveAsImage:{}}},xAxis:{type:"time",boundaryGap:!1},yAxis:{type:"value",boundaryGap:[0,"10%"],axisLabel:{formatter:function(t){return format_u_k_m(t)}}},dataZoom:[{type:"inside",start:65,end:100},{start:0,end:20}],series:[{name:"Spots",type:"line",smooth:!0,symbol:"none",itemStyle:{color:"#078513"},areaStyle:{color:new echarts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:"#57fa75"},{offset:1,color:"#118226"}])},data:s}]})})}constructor(t,e){var o=document.getElementById(t),o=echarts.init(o),r=(this.refresh(o,e),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){r.resize()})}}let plot_dst=new dx_spots_trend("chart-dx_spots_trend","/plot_get_dx_spots_trend");
|
1
static/js/rel/plot_hour_band.min.js
vendored
Normal file
1
static/js/rel/plot_hour_band.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class hour_band{refresh(i,t,s){fetch(t).then(t=>t.json()).then(a=>{var t=get_last_refresh(a),e=[];for(let t=23;-1<t;t--){var r={},o=t.toString();r.name=o,e.push(r)}var n=[];s.forEach(e=>{var r=[];for(let t=23;-1<t;t--)try{var o=a.hour_band[e][t];r.push(o=void 0===o?0:o)}catch(t){}n.push({value:r,name:e})}),i.setOption({legend:{orient:"horizontal",left:"left",bottom:"bottom"},title:{text:"DX SPOTS per hour in last month",subtext:t,top:"top",right:"right"},tooltip:{trigger:"axis"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},radar:{shape:"circle",startAngle:105,indicator:e,center:["47%","46%"],axisName:{color:"rgb(80,80,80)"}},series:[{lineStyle:{width:2},type:"radar",symbol:"none",data:n,tooltip:{trigger:"item",formatter:t=>"Band: "+t.name},emphasis:{lineStyle:{width:4}}}]})})}constructor(t,e,r){var o=document.getElementById(t),o=echarts.init(o),a=[],n=(r.forEach(function(t,e){a[e]=t.id}),this.refresh(o,e,a),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){n.resize()})}}let plot_hb=new hour_band("chart-hour_band","/plot_get_hour_band",band_frequencies);
|
1
static/js/rel/plot_world_dx_spots_live.min.js
vendored
Normal file
1
static/js/rel/plot_world_dx_spots_live.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class world_dx_spots_live{refresh(s,e){fetch(e).then(e=>e.json()).then(r=>{fetch("world.json").then(e=>e.text()).then(e=>{var t=get_last_refresh(r),o=[];r.world_dx_spots_live.forEach(function(e,t){o.push({value:[e.lat,e.lon,e.count]})}),s.hideLoading(),echarts.registerMap("WR",e),s.setOption({visualMap:{show:!1,min:0,max:50,inRange:{symbolSize:[5,20]}},geo:{type:"map",map:"WR",roam:!0,zoom:1.2,aspectScale:.7,layoutCenter:["50%","54%"],layoutSize:"100%",itemStyle:{normal:{areaColor:"#323c48",borderColor:"#111"},emphasis:{areaColor:"#2a333d"}},label:{emphasis:{show:!1}}},tooltip:{trigger:"item",formatter:function(e){return"Spots: <STRONG>"+e.value[2]+"</STRONG>"}},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",iconStyle:{borderColor:"#fff"},feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},restore:{show:!0},saveAsImage:{show:!0}}},legend:{show:!1},backgroundColor:"#596475",title:{text:"World DX SPOTS in last hour",subtext:t,top:"top",right:"right",textStyle:{color:"#fff"},subtextStyle:{color:"#fff"}},series:[{type:"scatter",coordinateSystem:"geo",data:o,label:{emphasis:{position:"right",show:!1}},itemStyle:{normal:{color:"#eea638"}}}]})})})}constructor(e,t){var o=document.getElementById(e),o=echarts.init(o),r=(this.refresh(o,t),echarts.init(document.querySelector("#"+e),null));window.addEventListener("resize",function(){r.resize()})}}let plot_wdsl=new world_dx_spots_live("chart-world_dx_spots_live","/plot_get_world_dx_spots_live");
|
1
static/js/rel/table.min.js
vendored
Normal file
1
static/js/rel/table.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,353 +0,0 @@
|
||||
class table_builder {
|
||||
|
||||
/**
|
||||
* Table builder constructor
|
||||
* @param selector {string} The html identifier where build the spots table
|
||||
*/
|
||||
constructor(selector) {
|
||||
this.selector=selector;
|
||||
this.current_data=[];
|
||||
}
|
||||
/**
|
||||
* @return last_rowid {integer} the last rowid
|
||||
*/
|
||||
getLastRowId() {
|
||||
let last_rowid;
|
||||
if (this.current_data == null) {
|
||||
last_rowid=0;
|
||||
} else {
|
||||
if (this.current_data.length < 1) {
|
||||
last_rowid=0;
|
||||
} else {
|
||||
last_rowid=this.current_data[0].rowid;
|
||||
}
|
||||
}
|
||||
return last_rowid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line {object} with the data of a single spot
|
||||
* @param isnew {boolean} is the new rows indicator
|
||||
* @param dt_current {string} current date in dd/mm/yyyy format
|
||||
* @param callsign {string} optional callsign
|
||||
* @return a <tr> dom element
|
||||
*/
|
||||
#buildRow(line, isnew, dt_current,callsign=""){
|
||||
const row = document.createElement("tr");
|
||||
row.id= line.rowid;
|
||||
|
||||
if (callsign.length>0) {
|
||||
if (callsign == line.de) {
|
||||
row.id= line.rowid;
|
||||
} else if (callsign == line.dx) {
|
||||
row.id= line.rowid;
|
||||
}
|
||||
} else if (isnew) {
|
||||
row.className="table-info";
|
||||
}
|
||||
|
||||
//Column: DE search on QRZ
|
||||
const i_qrzde = document.createElement("i");
|
||||
i_qrzde.className="bi-search";
|
||||
i_qrzde.role="button";
|
||||
i_qrzde.ariaLabel=line.de;
|
||||
const a_qrzde = document.createElement("a");
|
||||
a_qrzde.href=qrz_url + line.de;
|
||||
a_qrzde.target="_blank";
|
||||
a_qrzde.rel="noopener";
|
||||
const span_qrzde = document.createElement("span");
|
||||
|
||||
//Mark DE if it found in callsign search
|
||||
if (line.de == callsign) {
|
||||
const mark_qrzde = document.createElement("mark");
|
||||
mark_qrzde.textContent = line.de;
|
||||
span_qrzde.appendChild(mark_qrzde)
|
||||
} else {
|
||||
span_qrzde.textContent='\xa0' + line.de;
|
||||
}
|
||||
|
||||
const td_qrzde = document.createElement("td");
|
||||
|
||||
a_qrzde.appendChild(i_qrzde);
|
||||
td_qrzde.appendChild(a_qrzde);
|
||||
td_qrzde.appendChild(span_qrzde);
|
||||
row.append(td_qrzde);
|
||||
|
||||
//Column: frequency
|
||||
var freq = Intl.NumberFormat('it-IT', {
|
||||
style: 'decimal'
|
||||
}).format(line.freq);
|
||||
|
||||
const span_freq = document.createElement("span");
|
||||
span_freq.className="badge bg-warning text-dark badge-responsive";
|
||||
span_freq.textContent=freq;
|
||||
|
||||
const td_freq = document.createElement("td");
|
||||
td_freq.appendChild(span_freq);
|
||||
|
||||
row.appendChild(td_freq);
|
||||
|
||||
//Column: DX (with ADXO Management)
|
||||
var adxo = findAdxo(my_adxo_events, line.dx);
|
||||
var adxo_link = '<a href=' + adxo_url + ' target=_blank rel=noopener >NG3K Website</a>'
|
||||
const i_qrzdx = document.createElement("i");
|
||||
i_qrzdx.className="bi-search";
|
||||
i_qrzdx.role="button";
|
||||
i_qrzdx.ariaLabel=line.dx;
|
||||
const a_qrzdx = document.createElement("a");
|
||||
a_qrzdx.href=qrz_url + line.dx;
|
||||
a_qrzdx.target="_blank";
|
||||
a_qrzdx.rel="noopener";
|
||||
const span_qrzdx = document.createElement("span");
|
||||
|
||||
//Mark DX if it found in callsign search
|
||||
const mark_qrzdx = document.createElement("mark");
|
||||
mark_qrzdx.textContent = line.dx;
|
||||
if (line.dx == callsign) {
|
||||
span_qrzdx.appendChild(mark_qrzdx)
|
||||
} else {
|
||||
span_qrzdx.textContent='\xa0' + line.dx;
|
||||
}
|
||||
|
||||
if (adxo != undefined) {
|
||||
const i_adxo = document.createElement("i");
|
||||
i_adxo.tabIndex=0;
|
||||
i_adxo.className="bi-megaphone-fill";
|
||||
i_adxo.style="color: cornflowerblue;";
|
||||
i_adxo.role="button";
|
||||
i_adxo.ariaLabel="dx_operations";
|
||||
i_adxo.setAttribute('data-bs-container', "body" );
|
||||
i_adxo.setAttribute('data-bs-toggle', "popover" );
|
||||
i_adxo.setAttribute('data-bs-trigger', "focus" );
|
||||
i_adxo.setAttribute('data-bs-sanitizer', "true");
|
||||
i_adxo.setAttribute('data-bs-placement', "auto");
|
||||
i_adxo.setAttribute('data-bs-html', "true");
|
||||
i_adxo.setAttribute('data-bs-title', "Announced DX Op.: " + adxo.summary);
|
||||
i_adxo.setAttribute('data-bs-content', adxo.description + "data from " + adxo_link);
|
||||
span_qrzdx.appendChild(i_adxo);
|
||||
}
|
||||
|
||||
const td_qrzdx = document.createElement("td");
|
||||
a_qrzdx.appendChild(i_qrzdx);
|
||||
td_qrzdx.appendChild(a_qrzdx);
|
||||
td_qrzdx.append(span_qrzdx);
|
||||
row.appendChild(td_qrzdx);
|
||||
|
||||
//Column: Flag
|
||||
try {
|
||||
const span_flag=document.createElement("span");
|
||||
span_flag.className="img-flag fi fi-" + line.iso;
|
||||
span_flag.setAttribute('data-bs-container', "body" );
|
||||
span_flag.setAttribute('data-bs-toggle', "popover" );
|
||||
span_flag.setAttribute('data-bs-trigger', "hover" );
|
||||
span_flag.setAttribute('data-bs-placement', "left");
|
||||
span_flag.setAttribute('data-bs-content', line.country );
|
||||
|
||||
const td_flag = document.createElement("td");
|
||||
td_flag.appendChild(span_flag);
|
||||
row.appendChild(td_flag);
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log("error creating flag");
|
||||
const td_flag = document.createElement("td");
|
||||
row.appendChild(td_flag);
|
||||
}
|
||||
|
||||
//Column: Country
|
||||
const td_country_code = document.createElement("td");
|
||||
td_country_code.className="d-none d-lg-table-cell d-xl-table-cell";
|
||||
td_country_code.textContent = line.country;
|
||||
row.appendChild(td_country_code);
|
||||
|
||||
//Column: Comment
|
||||
const td_comm = document.createElement("td");
|
||||
td_comm.className="d-none d-lg-table-cell d-xl-table-cell";
|
||||
td_comm.textContent = line.comm;
|
||||
row.appendChild(td_comm);
|
||||
|
||||
//Column: UTC
|
||||
let dt = new Date(line.time * 1000);
|
||||
let hh = '00' + dt.getUTCHours();
|
||||
hh = hh.substring(hh.length - 2, hh.length);
|
||||
let mi = '00' + dt.getMinutes();
|
||||
mi = mi.substring(mi.length - 2, mi.length);
|
||||
let dd = '00' + dt.getUTCDate();
|
||||
dd = dd.substring(dd.length - 2, dd.length);
|
||||
let mo = '00' + (Number(dt.getUTCMonth()) + 1);
|
||||
mo = mo.substring(mo.length - 2, mo.length);
|
||||
let yy = dt.getUTCFullYear();
|
||||
let tm = hh + ':' + mi;
|
||||
dt = dd + '/' + mo + '/' + yy;
|
||||
|
||||
const div_date_time = document.createElement("div");
|
||||
div_date_time.className="d-flex flex-column";
|
||||
const p_time = document.createElement("div");
|
||||
p_time.textContent=tm;
|
||||
div_date_time.appendChild(p_time);
|
||||
if (dt != dt_current) {
|
||||
const p_date = document.createElement("div");
|
||||
p_date.textContent=dt;
|
||||
div_date_time.appendChild(p_date);
|
||||
}
|
||||
|
||||
row.appendChild(div_date_time);
|
||||
|
||||
//Finally append the row created to the table
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the table with the spot
|
||||
*
|
||||
* @param data {json} The payload with all the spots received from cluster
|
||||
* @param rl {json} Row List
|
||||
* @param callsign {string} An optional parameter with the callsign to search
|
||||
*/
|
||||
build(data, callsign) {
|
||||
|
||||
if (data != null) {
|
||||
|
||||
//get current date
|
||||
let d = new Date();
|
||||
let dd_current = '00' + d.getUTCDate();
|
||||
dd_current = dd_current.substring(dd_current.length - 2, dd_current.length);
|
||||
let mo_current = '00' + (Number(d.getUTCMonth()) + 1);
|
||||
mo_current = mo_current.substring(mo_current.length - 2, mo_current.length);
|
||||
let yy_current = d.getUTCFullYear();
|
||||
let dt_current = dd_current + '/' + mo_current + '/' + yy_current;
|
||||
|
||||
//empty the table
|
||||
document.getElementById(this.selector).replaceChildren();
|
||||
|
||||
//insert in table new elements
|
||||
let merge_data = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
document.getElementById(this.selector).append(this.#buildRow(data[i],true,dt_current,callsign));
|
||||
merge_data.push(data[i]);
|
||||
}
|
||||
|
||||
//insert in html table previous elements
|
||||
for (let i = 0; i < this.current_data.length-data.length; i++) {
|
||||
document.getElementById(this.selector).append(this.#buildRow(this.current_data[i],false,dt_current,callsign));
|
||||
merge_data.push(this.current_data[i]);
|
||||
}
|
||||
|
||||
//replace current data with merge
|
||||
this.current_data=merge_data;
|
||||
|
||||
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
|
||||
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
|
||||
return new bootstrap.Popover(popoverTriggerEl)
|
||||
})
|
||||
|
||||
try {
|
||||
return;
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} //end class
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
* javascript used to popolate main table with spots
|
||||
* ******************************************************************************/
|
||||
const adxo_url = 'https://www.ng3k.com/misc/adxo.html'
|
||||
const qrz_url = 'https://www.qrz.com/db/'
|
||||
const tb = new table_builder('bodyspot');
|
||||
|
||||
/**
|
||||
* Decode Announced Dx Operation (ng3k)
|
||||
*
|
||||
* @param adxo {adxo} This is the json containing all the dxo events
|
||||
* @param callsign_to_find {callsign_to_find} The callsign of the current dx line
|
||||
*/
|
||||
function findAdxo(adxo, callsign_to_find) {
|
||||
if (adxo) {
|
||||
for (let i = 0; i < adxo.length; i++) {
|
||||
if (adxo[i].callsign == callsign_to_find) {
|
||||
return adxo[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Function to filter spot when pressed the search button on filter
|
||||
* This function trigger the search, also triggered by timer
|
||||
*/
|
||||
function mySearch(event) {
|
||||
|
||||
event.preventDefault();
|
||||
refresh_timer(); //force the call of query
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for construct query string for single value selection
|
||||
*
|
||||
* @param id {string} The html identifier used for filter
|
||||
* @param param {string}the parameter for the query
|
||||
* @param len {number} The maximum number of element that could be selected; use -1 if the filter permits a single selection
|
||||
* @param qrystr {string} Th initial query string to be completed with the new filter
|
||||
*/
|
||||
function getFilter(id, param, len, qrystr) {
|
||||
|
||||
selectedFilter = [].map.call(document.getElementById(id).selectedOptions, option => option.value);
|
||||
var qryFilter = '';
|
||||
if (selectedFilter.length < len || len == -1) {
|
||||
qryFilter = selectedFilter.map(function(el) {
|
||||
if (el) {
|
||||
return param + '=' + el;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}).join('&');
|
||||
qrystr = qrystr.concat('&'.concat(qryFilter));
|
||||
if (qrystr.substring(0, 1) == '&') {
|
||||
qrystr = qrystr.substring(1)
|
||||
}
|
||||
}
|
||||
|
||||
return qrystr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search / Filter cluster spot based on filter settings
|
||||
* Gets the filter values, constructs the query parameter and
|
||||
* make the request to the server
|
||||
*/
|
||||
function refresh_timer() {
|
||||
|
||||
let qryAll = '';
|
||||
|
||||
//get other filters
|
||||
qryAll = getFilter('band', 'b', 14, qryAll);
|
||||
qryAll = getFilter('de_re', 'e', 7, qryAll);
|
||||
qryAll = getFilter('dx_re', 'x', 7, qryAll);
|
||||
qryAll = getFilter('mode', 'm', 3, qryAll);
|
||||
qryAll = getFilter('cqdeInput', 'qe', -1, qryAll);
|
||||
qryAll = getFilter('cqdxInput', 'qx', -1, qryAll);
|
||||
|
||||
//Composing query string
|
||||
let qryString = ('spotlist?lr=').concat(tb.getLastRowId());
|
||||
if (qryAll) {
|
||||
qryString = qryString.concat(qryAll);
|
||||
}
|
||||
|
||||
console.log(qryString)
|
||||
//Open a new connection, using the GET request on the URL endpoint
|
||||
fetch(qryString)
|
||||
.then((response) => response.json())
|
||||
.then((data_new) => {
|
||||
try {
|
||||
tb.build(data_new);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(err.stack);
|
||||
console.log(data_new);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
13
static/js/table.min.js
vendored
13
static/js/table.min.js
vendored
@ -1,13 +0,0 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(a,b,c){a instanceof String&&(a=String(a));for(var d=a.length,k=0;k<d;k++){var g=a[k];if(b.call(c,g,k,a))return{i:k,v:g}}return{i:-1,v:void 0}};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
|
||||
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var k=a[d];k in c||(c[k]={});c=c[k]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,c){return $jscomp.findInternal(this,a,c).v}},"es6","es3");
|
||||
$jscomp.polyfill("Array.from",function(a){return a?a:function(a,c,d){c=null!=c?c:function(a){return a};var b=[],g="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];if("function"==typeof g){a=g.call(a);for(var f=0;!(g=a.next()).done;)b.push(c.call(d,g.value,f++))}else for(g=a.length,f=0;f<g;f++)b.push(c.call(d,a[f],f));return b}},"es6","es3");var adxo_url="https://www.ng3k.com/misc/adxo.html",qrz_url="https://www.qrz.com/db/";
|
||||
function findAdxo(a,b){if(a)for(var c=0;c<a.length;c++)if(a[c].callsign==b)return a[c]}
|
||||
function buildHtmlTable(a,b,c,d){if(null!=b){var k=[],g=new Date,f="00"+g.getUTCDate();f=f.substring(f.length-2,f.length);var h="00"+(Number(g.getUTCMonth())+1);h=h.substring(h.length-2,h.length);g=g.getUTCFullYear();dt_current=f+"/"+h+"/"+g;$(a).empty();for(var e=0;e<b.length;e++){k[e]=b[e].rowid;f=$('<tr id="'+b[e].rowid+'"/>');h=c.find(function(a){return a==b[e].rowid});void 0!=d?d==b[e].de?f=$('<tr id="'+b[e].rowid+'"/>'):d==b[e].dx&&(f=$('<tr id="'+b[e].rowid+'"/>')):void 0==h&&0<c.length&&(f=
|
||||
$('<tr class="table-info" id="'+b[e].rowid+'"/>'));de=b[e].de==d?"<mark>"+b[e].de+"</mark>":b[e].de;f.append($("<td/>").html('<a href="'+qrz_url+b[e].de+'" target="_blank" rel="noopener"><i class="bi-search" role="button" aria-label="'+b[e].de+'"></i></a><span> '+de+"</span></b>"));h=Intl.NumberFormat("it-IT",{style:"decimal"}).format(b[e].freq);f.append($("<td/>").html('<span class="badge bg-warning text-dark badge-responsive">'+h+"</span>"));dx=b[e].dx==d?"<mark>"+b[e].dx+"</mark>":b[e].dx;
|
||||
h=findAdxo(my_adxo_events,b[e].dx);g="<a href="+adxo_url+" target=_blank rel=noopener >NG3K Website</a>";void 0!=h&&(dx=dx+' <i tabindex="0" class="bi-megaphone-fill" style="color: cornflowerblue; " role="button" aria-label="dx_operations" data-bs-container="body" data-bs-toggle="popover" data-bs-trigger="focus" data-bs-sanitize="true" data-bs-placement="auto" data-bs-html="true" data-bs-title="Announced DX Op.: '+h.summary+'" data-bs-content="'+h.description+" data from  "+g+'"></i>');f.append($("<td/>").html('<a href="'+
|
||||
qrz_url+b[e].dx+'" target="_blank" rel="noopener"><i class="bi-search" role="button" aria-label="'+b[e].dx+'"></i></a><span> '+dx+"</span>"));try{f.append($("<td/>").html('<span class="img-flag fi fi-'+b[e].iso+'" data-bs-container="body" data-bs-toggle="popover" data-bs-trigger="hover" data-bs-placement="left" data-bs-content="'+b[e].country+'"></span>'))}catch(p){f.append($("<td/>"))}f.append($('<td class="d-none d-lg-table-cell d-xl-table-cell"/>').html(b[e].country));f.append($('<td class="d-none d-lg-table-cell d-xl-table-cell"/>').html(b[e].comm));
|
||||
var l=new Date(1E3*b[e].time);h="00"+l.getUTCHours();h=h.substring(h.length-2,h.length);g="00"+l.getMinutes();g=g.substring(g.length-2,g.length);var m="00"+l.getUTCDate();m=m.substring(m.length-2,m.length);var n="00"+(Number(l.getUTCMonth())+1);n=n.substring(n.length-2,n.length);l=l.getUTCFullYear();tm=h+":"+g;l=m+"/"+n+"/"+l;l==dt_current?f.append($("<td/>").html(tm)):f.append($("<td/>").html('<table class="table-sm table-borderless"><tbody><tr style="background-color:transparent"><td>'+tm+"</td></tr><tr><td>"+
|
||||
l+"</td></tr></tbody></table>"));$(a).append(f)}$(function(){$('[data-bs-toggle="popover"]').popover({container:a})});try{return Array.from(k)}catch(p){}}}function mySearch(a){a.preventDefault();myTimer()}
|
||||
function getFilter(a,b,c,d){selectedFilter=[].map.call(document.getElementById(a).selectedOptions,function(a){return a.value});a="";if(selectedFilter.length<c||-1==c)a=selectedFilter.map(function(a){return a?b+"="+a:""}).join("&"),d=d.concat("&".concat(a)),"&"==d.substring(0,1)&&(d=d.substring(1));return d}
|
||||
function myTimer(){var a=getFilter("band","b",14,"");a=getFilter("de_re","e",7,a);a=getFilter("dx_re","x",7,a);a=getFilter("mode","m",3,a);a=getFilter("cqdeInput","qe",-1,a);a=getFilter("cqdxInput","qx",-1,a);var b="spotlist";a&&(b=b.concat("?".concat(a)));fetch(b).then(function(a){return a.json()}).then(function(a){try{rows_list=buildHtmlTable("#bodyspot",a,rows_list)}catch(d){console.log(d),console.log(d.stack),console.log(a)}})};
|
@ -13,7 +13,7 @@
|
||||
<link rel="icon" href="/static/images/icons/spider_ico_master.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/images/icons/icon-apple.png">
|
||||
<link rel="manifest" href="/static/manifest.webmanifest">
|
||||
<link rel="stylesheet" href="/static/css/style.min.css">
|
||||
<link rel="stylesheet" href="/static/css/rel/style.min.css">
|
||||
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" as="style" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous" onload="this.rel='stylesheet' ">
|
||||
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"></noscript>
|
||||
@ -21,7 +21,7 @@
|
||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css" as="style" integrity="sha512-uvXdJud8WaOlQFjlz9B15Yy2Au/bMAvz79F7Xa6OakCl2jvQPdHD0hb3dEqZRdSwG4/sknePXlE7GiarwA/9Wg==" crossorigin="anonymous" onload="this.rel='stylesheet'" >
|
||||
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css"></noscript>
|
||||
|
||||
<script class="spiderscript" src="static/js/load_css.min.js"></script>
|
||||
<script src="static/js/rel/load_css.min.js"></script>
|
||||
{% endblock head %}
|
||||
</head>
|
||||
<body>
|
||||
@ -89,21 +89,18 @@
|
||||
<span id="version">v2.4.1</span>
|
||||
</div>
|
||||
</footer>
|
||||
<script async class="spiderscript" src="static/js/clock.min.js"></script>
|
||||
<script async class="spiderscript" src="static/js/copy_date.min.js"></script>
|
||||
<script async class="spiderscript" src="static/js/load-sw.min.js"></script>
|
||||
<script async src="static/js/rel/clock.min.js"></script>
|
||||
<script async src="static/js/rel/copy_date.min.js"></script>
|
||||
<script async src="static/js/rel/load-sw.min.js"></script>
|
||||
<script nonce="sedfGFG32xs">
|
||||
{% block app_data %}
|
||||
var my_callsign='{{callsign}}';
|
||||
{% endblock app_data %}
|
||||
</script>
|
||||
<script defer class="spiderscript" src="static/js/common.js"></script>
|
||||
<!--
|
||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
-->
|
||||
<script defer src="static/js/rel/common.min.js"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
|
||||
{% block app_scripts %}
|
||||
<script async class="spiderscript" src="static/js/callsign_search.js"></script>
|
||||
<script async src="static/js/rel/callsign_search.min.js"></script>
|
||||
{% endblock app_scripts %}
|
||||
{% block inline_scripts %}
|
||||
{% endblock inline_scripts %}
|
||||
@ -131,7 +128,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script defer class="spiderscript" src="static/js/cookie_consent.js"></script>
|
||||
<script defer src="static/js/rel/cookie_consent.min.js"></script>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% extends "index.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>Spot search for a specific Callsign</title>
|
||||
@ -12,5 +13,5 @@
|
||||
<div class="row mx-auto">
|
||||
{% endblock filters %}
|
||||
{% block inline_scripts %}
|
||||
<script defer class="spiderscript" src="static/js/callsign_inline.js"></script>
|
||||
<script defer src="static/js/rel/callsign_inline.min.js"></script>
|
||||
{% endblock %}
|
@ -1,5 +0,0 @@
|
||||
<input type="checkbox" id="category_{{ category.name }}"
|
||||
{% if category.default %}checked="checked"{% endif %}
|
||||
{% if category.is_required %}disabled="disabled"{% endif %}
|
||||
name="flask_consent_category" value="{{ category.name }}"/>
|
||||
<label for="category_{{ category.name }}">{{ category.title }}</label>
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>DX Cluster from IU1BOW: Cookies</title>
|
||||
@ -15,16 +16,29 @@
|
||||
{% endblock %}
|
||||
{% block contents %}
|
||||
<div class="col mr-3 px-2">
|
||||
<p class="text-justify"><span class="font-weight-bold">Cookies</span> are small text files that can be used by websites to make a user's experience more efficient. This site uses different types of cookies. You can at any time change or withdraw
|
||||
your consent from the Cookies page on my website. Some cookies are placed by third party services that appear on our pages, for example if you view or listen to any embedded audio or video content. I don't control the setting of these cookies, so
|
||||
please check the websites of these third parties for more information about their cookies and how they manage them.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Necessary</span> cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without
|
||||
<p class="text-justify"><span class="font-weight-bold">Cookies</span> are small text files that can be used by
|
||||
websites to make a user's experience more efficient. This site uses different types of cookies. You can at any time
|
||||
change or withdraw
|
||||
your consent from the Cookies page on my website. Some cookies are placed by third party services that appear on our
|
||||
pages, for example if you view or listen to any embedded audio or video content. I don't control the setting of
|
||||
these cookies, so
|
||||
please check the websites of these third parties for more information about their cookies and how they manage them.
|
||||
</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Necessary</span> cookies help make a website usable by enabling
|
||||
basic functions like page navigation and access to secure areas of the website. The website cannot function properly
|
||||
without
|
||||
these cookies.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Preference</span> cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Statistic</span> cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Marketing</span> cookies are used to track visitors across websites. The intention is to display content, as well as ads that are relevant and engaging for the individual user and thereby more
|
||||
<p class="text-justify"><span class="font-weight-bold">Preference</span> cookies enable a website to remember
|
||||
information that changes the way the website behaves or looks, like your preferred language or the region that you
|
||||
are in.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Statistic</span> cookies help website owners to understand how
|
||||
visitors interact with websites by collecting and reporting information anonymously.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Marketing</span> cookies are used to track visitors across
|
||||
websites. The intention is to display content, as well as ads that are relevant and engaging for the individual user
|
||||
and thereby more
|
||||
valuable for publishers and third party advertisers.</p>
|
||||
<p class="text-justify"><span class="font-weight-bold">Unclassified</span> cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.</p>
|
||||
<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 %}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>DX Cluster / DX Spot for Hamradio</title>
|
||||
@ -14,7 +15,8 @@
|
||||
{% block filters %}
|
||||
<div class="row mx-auto justify-content-between align-middle">
|
||||
<div class="mx-auto">
|
||||
<button class="btn btn-primary btn-sm" type="button" data-toggle="collapse" aria-expanded="false" aria-label="filter" data-bs-toggle="collapse" data-bs-target="#collapseFilters">
|
||||
<button class="btn btn-primary btn-sm" type="button" data-toggle="collapse" aria-expanded="false"
|
||||
aria-label="filter" data-bs-toggle="collapse" data-bs-target="#collapseFilters">
|
||||
<span class="bi-funnel-fill" role="button" aria-label="funnel-fill"></span>
|
||||
</button>
|
||||
</div>
|
||||
@ -162,7 +164,9 @@
|
||||
{% endif %}
|
||||
<p></p>
|
||||
<div class="mx-auto">
|
||||
<button type="submit" class="btn btn-primary btn-block w-100" aria-pressed="true" data-toggle="collapse" data-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters" aria-label="filter">Search</button>
|
||||
<button type="submit" class="btn btn-primary btn-block w-100" aria-pressed="true" data-toggle="collapse"
|
||||
data-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters"
|
||||
aria-label="filter">Search</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -200,11 +204,8 @@
|
||||
{% endblock app_data %}
|
||||
{% block app_scripts %}
|
||||
{{ super() }}
|
||||
<script defer class="spiderscript" src="static/js/table.js"></script>
|
||||
<script defer src="static/js/rel/table.min.js"></script>
|
||||
{% endblock %}
|
||||
{% block inline_scripts %}
|
||||
<script defer class="spiderscript" src="static/js/index_inline.js"></script>
|
||||
<script defer src="static/js/rel/index_inline.min.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>DX Cluster from IU1BOW: OFFLINE</title>
|
||||
@ -25,7 +26,8 @@
|
||||
<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">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>
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>Some charts end stats from the dx clustes node</title>
|
||||
@ -6,7 +7,9 @@
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<meta http-equiv="refresh" content="300">
|
||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.1/echarts.min.js" integrity="sha512-OTbGFYPLe3jhy4bUwbB8nls0TFgz10kn0TLkmyA+l3FyivDs31zsXCjOis7YGDtE2Jsy0+fzW+3/OVoPVujPmQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.1/echarts.min.js"
|
||||
integrity="sha512-OTbGFYPLe3jhy4bUwbB8nls0TFgz10kn0TLkmyA+l3FyivDs31zsXCjOis7YGDtE2Jsy0+fzW+3/OVoPVujPmQ=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
{% endblock %}
|
||||
|
||||
</head>
|
||||
@ -42,16 +45,14 @@
|
||||
<div class="shadow-lg mb-5 bg-body rounded" id="chart-hour_band"></div>
|
||||
|
||||
<a class="shadow-lg mb-5 bg-body rounded" href="https://sidc.be/silso/" target="_blank" rel="noopener noreferrer">
|
||||
<img src="https://sidc.be/silso/IMAGES/GRAPHICS/prediSC.png"class="img-fluid" id="silo-propagation-img" alt="propagation trend">
|
||||
<img src="https://sidc.be/silso/IMAGES/GRAPHICS/prediSC.png" class="img-fluid" id="silo-propagation-img"
|
||||
alt="propagation trend">
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<div class="shadow-lg mb-5 bg-body rounded" id="chart-dx_spots_x_month"></div>
|
||||
|
||||
<div class="shadow-lg mb-5 bg-body rounded" id="chart-dx_spots_trend"></div>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="shadow-lg mb-5 bg-body rounded">
|
||||
<strong>Physically connected callsigns to {{ mycallsign }}</strong>
|
||||
@ -91,10 +92,10 @@
|
||||
|
||||
{% block app_scripts %}
|
||||
{{ super() }}
|
||||
<script defer class="spiderscript" src="static/js/plot_band_activity.js"></script>
|
||||
<script defer class="spiderscript" src="static/js/plot_world_dx_spots_live.js"></script>
|
||||
<script defer class="spiderscript" src="static/js/plot_hour_band.js"></script>
|
||||
<script defer class="spiderscript" src="static/js/plot_dx_spots_trend.js"></script>
|
||||
<script defer class="spiderscript" src="static/js/plot_dx_spots_per_month.js"></script>
|
||||
<script defer src="static/js/rel/plot_band_activity.min.js"></script>
|
||||
<script defer src="static/js/rel/plot_world_dx_spots_live.min.js"></script>
|
||||
<script defer src="static/js/rel/plot_hour_band.min.js"></script>
|
||||
<script defer src="static/js/rel/plot_dx_spots_trend.min.js"></script>
|
||||
<script defer src="static/js/rel/plot_dx_spots_per_month.min.js"></script>
|
||||
|
||||
{% endblock app_scripts %}
|
@ -1,4 +1,5 @@
|
||||
{% extends "_base.html" %}
|
||||
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>DX Cluster from IU1BOW: Privacy</title>
|
||||
@ -16,15 +17,27 @@
|
||||
{% block contents %}
|
||||
<div class="col mr-3 px-2">
|
||||
<h1>Privacy Policy for this web site</h1>
|
||||
<p>At this web site, one of our main priorities is the privacy of our visitors. This Privacy Policy document contains types of information that is collected and recorded by this web site and how we use it.</p>
|
||||
<p>If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact us.</p>
|
||||
<p>This Privacy Policy applies only to our online activities and is valid for visitors to our website with regards to the information that they shared and/or collect in this web site. This policy is not applicable to any information collected offline or via channels other than this website. Our Privacy Policy was created with the help of the <a href="https://www.privacypolicygenerator.info">Privacy Policy Generator</a> and the <a href="https://www.generateprivacypolicy.com/">Free Privacy Policy Generator</a>.</p>
|
||||
<p>At this web site, one of our main priorities is the privacy of our visitors. This Privacy Policy document
|
||||
contains types of information that is collected and recorded by this web site and how we use it.</p>
|
||||
<p>If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact
|
||||
us.</p>
|
||||
<p>This Privacy Policy applies only to our online activities and is valid for visitors to our website with regards
|
||||
to the information that they shared and/or collect in this web site. This policy is not applicable to any
|
||||
information collected offline or via channels other than this website. Our Privacy Policy was created with the
|
||||
help of the <a href="https://www.privacypolicygenerator.info">Privacy Policy Generator</a> and the <a
|
||||
href="https://www.generateprivacypolicy.com/">Free Privacy Policy Generator</a>.</p>
|
||||
<h2>Consent</h2>
|
||||
<p>By using our website, you hereby consent to our Privacy Policy and agree to its terms. For our Terms and Conditions, please visit the <a href="https://www.privacypolicyonline.com/terms-conditions-generator/">Terms & Conditions Generator</a>.</p>
|
||||
<p>By using our website, you hereby consent to our Privacy Policy and agree to its terms. For our Terms and
|
||||
Conditions, please visit the <a href="https://www.privacypolicyonline.com/terms-conditions-generator/">Terms &
|
||||
Conditions Generator</a>.</p>
|
||||
<h2>Information we collect</h2>
|
||||
<p>The personal information that you are asked to provide, and the reasons why you are asked to provide it, will be made clear to you at the point we ask you to provide your personal information.</p>
|
||||
<p>If you contact us directly, we may receive additional information about you such as your name, email address, phone number, the contents of the message and/or attachments you may send us, and any other information you may choose to provide.</p>
|
||||
<p>When you register for an Account, we may ask for your contact information, including items such as name, company name, address, email address, and telephone number.</p>
|
||||
<p>The personal information that you are asked to provide, and the reasons why you are asked to provide it, will be
|
||||
made clear to you at the point we ask you to provide your personal information.</p>
|
||||
<p>If you contact us directly, we may receive additional information about you such as your name, email address,
|
||||
phone number, the contents of the message and/or attachments you may send us, and any other information you may
|
||||
choose to provide.</p>
|
||||
<p>When you register for an Account, we may ask for your contact information, including items such as name, company
|
||||
name, address, email address, and telephone number.</p>
|
||||
<h2>How we use your information</h2>
|
||||
<p>We use the information we collect in various ways, including to:</p>
|
||||
<ul>
|
||||
@ -32,40 +45,72 @@
|
||||
<li>Improve, personalize, and expand our webste</li>
|
||||
<li>Understand and analyze how you use our webste</li>
|
||||
<li>Develop new products, services, features, and functionality</li>
|
||||
<li>Communicate with you, either directly or through one of our partners, including for customer service, to provide you with updates and other information relating to the webste, and for marketing and promotional purposes</li>
|
||||
<li>Communicate with you, either directly or through one of our partners, including for customer service, to
|
||||
provide you with updates and other information relating to the webste, and for marketing and promotional
|
||||
purposes</li>
|
||||
<li>Send you emails</li>
|
||||
<li>Find and prevent fraud</li>
|
||||
</ul>
|
||||
<h2>Log Files</h2>
|
||||
<p>this web site follows a standard procedure of using log files. These files log visitors when they visit websites. All hosting companies do this and a part of hosting services' analytics. The information collected by log files include internet protocol (IP) addresses, browser type, Internet Service Provider (ISP), date and time stamp, referring/exit pages, and possibly the number of clicks. These are not linked to any information that is personally identifiable. The purpose of the information is for analyzing trends, administering the site, tracking users' movement on the website, and gathering demographic information.</p>
|
||||
<p>this web site follows a standard procedure of using log files. These files log visitors when they visit websites.
|
||||
All hosting companies do this and a part of hosting services' analytics. The information collected by log files
|
||||
include internet protocol (IP) addresses, browser type, Internet Service Provider (ISP), date and time stamp,
|
||||
referring/exit pages, and possibly the number of clicks. These are not linked to any information that is
|
||||
personally identifiable. The purpose of the information is for analyzing trends, administering the site,
|
||||
tracking users' movement on the website, and gathering demographic information.</p>
|
||||
<h2>Cookies and Web Beacons</h2>
|
||||
<p>Like any other website, this web site uses 'cookies'. These cookies are used to store information including visitors' preferences, and the pages on the website that the visitor accessed or visited. The information is used to optimize the users' experience by customizing our web page content based on visitors' browser type and/or other information.</p>
|
||||
<p>For more general information on cookies, please read <a href="https://www.privacypolicies.com/blog/cookies/">"What Are Cookies"</a>.</p>
|
||||
<p>Like any other website, this web site uses 'cookies'. These cookies are used to store information including
|
||||
visitors' preferences, and the pages on the website that the visitor accessed or visited. The information is
|
||||
used to optimize the users' experience by customizing our web page content based on visitors' browser type
|
||||
and/or other information.</p>
|
||||
<p>For more general information on cookies, please read <a
|
||||
href="https://www.privacypolicies.com/blog/cookies/">"What Are Cookies"</a>.</p>
|
||||
<h2>Advertising Partners Privacy Policies</h2>
|
||||
<P>You may consult this list to find the Privacy Policy for each of the advertising partners of this web site.</p>
|
||||
<p>Third-party ad servers or ad networks uses technologies like cookies, JavaScript, or Web Beacons that are used in their respective advertisements and links that appear on this web site, which are sent directly to users' browser. They automatically receive your IP address when this occurs. These technologies are used to measure the effectiveness of their advertising campaigns and/or to personalize the advertising content that you see on websites that you visit.</p>
|
||||
<p>Note that this web site has no access to or control over these cookies that are used by third-party advertisers.</p>
|
||||
<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>
|
||||
<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 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>
|
||||
<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>
|
||||
<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>
|
||||
<p>Another part of our priority is adding protection for children while using the internet. We encourage parents and
|
||||
guardians to observe, participate in, and/or monitor and guide their online activity.</p>
|
||||
<p>this web site does not knowingly collect any Personal Identifiable Information from children under the age of 13.
|
||||
If you think that your child provided this kind of information on our website, we strongly encourage you to
|
||||
contact us immediately and we will do our best efforts to promptly remove such information from our records.</p>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
21
test.sh
21
test.sh
@ -1,8 +1,21 @@
|
||||
if [ "$1" == "-b" ]; then
|
||||
if [ $# -gt 0 ]
|
||||
then
|
||||
cd scripts || exit
|
||||
./build.sh
|
||||
if ! ./build.sh ${1}
|
||||
then
|
||||
cd ..
|
||||
echo "terminated"
|
||||
exit 1
|
||||
fi
|
||||
cd ..
|
||||
fi
|
||||
#python3 webapp.py
|
||||
flask --app webapp.py run
|
||||
|
||||
if [ "$1" == "-d" ]; then
|
||||
flask --app webapp.py --debug run
|
||||
else
|
||||
flask --app webapp.py run
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
292
webapp.py
292
webapp.py
@ -1,4 +1,4 @@
|
||||
__author__ = 'IU1BOW - Corrado'
|
||||
__author__ = "IU1BOW - Corrado"
|
||||
import os
|
||||
import flask
|
||||
from flask import request, render_template, jsonify
|
||||
@ -23,41 +23,44 @@ logger = logging.getLogger(__name__)
|
||||
logger.info("Start")
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.config["DEBUG"] = False
|
||||
app.config['SECRET_KEY'] = 'secret!'
|
||||
app.config["SECRET_KEY"] = "secret!"
|
||||
app.config.update(
|
||||
SESSION_COOKIE_SECURE=True, #If you use http change it to False
|
||||
SESSION_COOKIE_SECURE=True,
|
||||
SESSION_COOKIE_HTTPONLY=True,
|
||||
SESSION_COOKIE_SAMESITE='Strict',
|
||||
SESSION_COOKIE_SAMESITE="Strict",
|
||||
)
|
||||
|
||||
csrf = CSRFProtect(app)
|
||||
#minify(app=app, html=True, js=True,cssless=False)
|
||||
logger.debug(app.config)
|
||||
|
||||
if app.config["DEBUG"]:
|
||||
minify(app=app, html=False, js=False, cssless=False)
|
||||
else:
|
||||
minify(app=app, html=True, js=True, cssless=False)
|
||||
|
||||
# load config file
|
||||
with open('cfg/config.json') as json_data_file:
|
||||
with open("cfg/config.json") as json_data_file:
|
||||
cfg = json.load(json_data_file)
|
||||
|
||||
logging.debug("CFG:")
|
||||
logging.debug(cfg)
|
||||
# load bands file
|
||||
with open('cfg/bands.json') as json_bands:
|
||||
with open("cfg/bands.json") as json_bands:
|
||||
band_frequencies = json.load(json_bands)
|
||||
|
||||
# load mode file
|
||||
with open('cfg/modes.json') as json_modes:
|
||||
with open("cfg/modes.json") as json_modes:
|
||||
modes_frequencies = json.load(json_modes)
|
||||
|
||||
# load continents-cq file
|
||||
with open('cfg/continents.json') as json_continents:
|
||||
with open("cfg/continents.json") as json_continents:
|
||||
continents_cq = json.load(json_continents)
|
||||
|
||||
# read and set default for enabling cq filter
|
||||
if cfg.get('enable_cq_filter'):
|
||||
enable_cq_filter=cfg['enable_cq_filter'].upper()
|
||||
if cfg.get("enable_cq_filter"):
|
||||
enable_cq_filter = cfg["enable_cq_filter"].upper()
|
||||
else:
|
||||
enable_cq_filter='N'
|
||||
enable_cq_filter = "N"
|
||||
|
||||
# define country table for search info on callsigns
|
||||
pfxt = prefix_table()
|
||||
@ -67,96 +70,115 @@ qm=query_manager()
|
||||
|
||||
# find id in json : ie frequency / continent
|
||||
def find_id_json(json_object, name):
|
||||
return [obj for obj in json_object if obj['id']==name][0]
|
||||
return [obj for obj in json_object if obj["id"] == name][0]
|
||||
|
||||
|
||||
def query_build_callsign(callsign):
|
||||
|
||||
query_string=''
|
||||
query_string = ""
|
||||
if len(callsign) <= 14:
|
||||
query_string="(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotter='"+callsign+"'"
|
||||
query_string = (
|
||||
"(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotter='"
|
||||
+ callsign
|
||||
+ "'"
|
||||
)
|
||||
query_string += " ORDER BY rowid desc limit 10)"
|
||||
query_string += " UNION "
|
||||
query_string+="(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotcall='"+callsign+"'"
|
||||
query_string += (
|
||||
"(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotcall='"
|
||||
+ callsign
|
||||
+ "'"
|
||||
)
|
||||
query_string += " ORDER BY rowid desc limit 10);"
|
||||
else:
|
||||
logging.warning('callsign too long')
|
||||
logging.warning("callsign too long")
|
||||
return query_string
|
||||
|
||||
|
||||
def query_build():
|
||||
|
||||
try:
|
||||
# get url parameters
|
||||
last_rowid=request.args.get('lr') #Last rowid fetched by front end
|
||||
band=(request.args.getlist('b')) #band filter
|
||||
dere=(request.args.getlist('e')) #DE continent filter
|
||||
dxre=(request.args.getlist('x')) #Dx continent filter
|
||||
mode=(request.args.getlist('m')) #mode filter
|
||||
decq=(request.args.getlist('qe')) #DE cq zone filter
|
||||
dxcq=(request.args.getlist('qx')) #DX cq zone filter
|
||||
last_rowid = request.args.get("lr") # Last rowid fetched by front end
|
||||
band = request.args.getlist("b") # band filter
|
||||
dere = request.args.getlist("e") # DE continent filter
|
||||
dxre = request.args.getlist("x") # Dx continent filter
|
||||
mode = request.args.getlist("m") # mode filter
|
||||
decq = request.args.getlist("qe") # DE cq zone filter
|
||||
dxcq = request.args.getlist("qx") # DX cq zone filter
|
||||
|
||||
|
||||
query_string=''
|
||||
query_string = ""
|
||||
|
||||
# construct band query decoding frequencies with json file
|
||||
band_qry_string = ' AND (('
|
||||
band_qry_string = " AND (("
|
||||
for i, item_band in enumerate(band):
|
||||
freq = find_id_json(band_frequencies["bands"], item_band)
|
||||
if i > 0:
|
||||
band_qry_string += ') OR ('
|
||||
band_qry_string += ") OR ("
|
||||
|
||||
band_qry_string += 'freq BETWEEN ' + str(freq["min"]) + ' AND ' + str(freq["max"])
|
||||
band_qry_string += (
|
||||
"freq BETWEEN " + str(freq["min"]) + " AND " + str(freq["max"])
|
||||
)
|
||||
|
||||
band_qry_string += '))'
|
||||
band_qry_string += "))"
|
||||
|
||||
# construct mode query
|
||||
mode_qry_string = ' AND (('
|
||||
mode_qry_string = " AND (("
|
||||
for i, item_mode in enumerate(mode):
|
||||
single_mode = find_id_json(modes_frequencies["modes"], item_mode)
|
||||
if i > 0:
|
||||
mode_qry_string +=') OR ('
|
||||
mode_qry_string += ") OR ("
|
||||
for j in range(len(single_mode["freq"])):
|
||||
if j > 0:
|
||||
mode_qry_string +=') OR ('
|
||||
mode_qry_string += 'freq BETWEEN ' +str(single_mode["freq"][j]["min"]) + ' AND ' + str(single_mode["freq"][j]["max"])
|
||||
mode_qry_string += ") OR ("
|
||||
mode_qry_string += (
|
||||
"freq BETWEEN "
|
||||
+ str(single_mode["freq"][j]["min"])
|
||||
+ " AND "
|
||||
+ str(single_mode["freq"][j]["max"])
|
||||
)
|
||||
|
||||
mode_qry_string += '))'
|
||||
mode_qry_string += "))"
|
||||
|
||||
# construct DE continent region query
|
||||
dere_qry_string = ' AND spottercq IN ('
|
||||
dere_qry_string = " AND spottercq IN ("
|
||||
for i, item_dere in enumerate(dere):
|
||||
continent = find_id_json(continents_cq["continents"], item_dere)
|
||||
if i > 0:
|
||||
dere_qry_string +=','
|
||||
dere_qry_string += ","
|
||||
dere_qry_string += str(continent["cq"])
|
||||
dere_qry_string +=')'
|
||||
dere_qry_string += ")"
|
||||
|
||||
# construct DX continent region query
|
||||
dxre_qry_string = ' AND spotcq IN ('
|
||||
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 += ","
|
||||
dxre_qry_string += str(continent["cq"])
|
||||
dxre_qry_string +=')'
|
||||
dxre_qry_string += ")"
|
||||
|
||||
if enable_cq_filter == 'Y':
|
||||
if enable_cq_filter == "Y":
|
||||
# construct de cq query
|
||||
decq_qry_string = ''
|
||||
decq_qry_string = ""
|
||||
if len(decq) == 1:
|
||||
if decq[0].isnumeric():
|
||||
decq_qry_string = ' AND spottercq =' + decq[0]
|
||||
decq_qry_string = " AND spottercq =" + decq[0]
|
||||
# construct dx cq query
|
||||
dxcq_qry_string = ''
|
||||
dxcq_qry_string = ""
|
||||
if len(dxcq) == 1:
|
||||
if dxcq[0].isnumeric():
|
||||
dxcq_qry_string = ' AND spotcq =' + dxcq[0]
|
||||
dxcq_qry_string = " AND spotcq =" + dxcq[0]
|
||||
|
||||
if last_rowid is None:
|
||||
last_rowid = "0"
|
||||
if not last_rowid.isnumeric():
|
||||
last_rowid = 0
|
||||
|
||||
query_string="SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE rowid > "+last_rowid
|
||||
query_string = (
|
||||
"SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE rowid > "
|
||||
+ last_rowid
|
||||
)
|
||||
|
||||
if len(band) > 0:
|
||||
query_string += band_qry_string
|
||||
@ -170,7 +192,7 @@ def query_build():
|
||||
if len(dxre) > 0:
|
||||
query_string += dxre_qry_string
|
||||
|
||||
if enable_cq_filter == 'Y':
|
||||
if enable_cq_filter == "Y":
|
||||
if len(decq_qry_string) > 0:
|
||||
query_string += decq_qry_string
|
||||
|
||||
@ -181,17 +203,18 @@ def query_build():
|
||||
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
query_string = ''
|
||||
query_string = ""
|
||||
|
||||
return query_string
|
||||
|
||||
|
||||
# the main query to show spots
|
||||
# it gets url parameter in order to apply the build the right query
|
||||
# and apply the filter required. It returns a json with the spots
|
||||
def spotquery():
|
||||
try:
|
||||
|
||||
callsign=request.args.get('c') #search specific callsign
|
||||
callsign = request.args.get("c") # search specific callsign
|
||||
|
||||
if callsign:
|
||||
query_string = query_build_callsign(callsign)
|
||||
@ -224,14 +247,17 @@ def spotquery():
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
|
||||
# find adxo events
|
||||
adxo_events = None
|
||||
|
||||
|
||||
def get_adxo():
|
||||
global adxo_events
|
||||
adxo_events = get_adxo_events()
|
||||
threading.Timer(12 * 3600, get_adxo).start()
|
||||
|
||||
|
||||
get_adxo()
|
||||
|
||||
# create data provider for charts
|
||||
@ -242,82 +268,147 @@ bubble_graph_hb=HourBand(logger,qm,band_frequencies)
|
||||
geo_graph_wdsl = WorldDxSpotsLive(logger, qm, pfxt)
|
||||
|
||||
# ROUTINGS
|
||||
@app.route('/spotlist', methods=['GET'])
|
||||
@app.route("/spotlist", methods=["GET"])
|
||||
def spotlist():
|
||||
response = flask.Response(json.dumps(spotquery()))
|
||||
return response
|
||||
|
||||
|
||||
def who_is_connected():
|
||||
host_port=cfg['telnet'].split(':')
|
||||
response=who(host_port[0],host_port[1],cfg['mycallsign'])
|
||||
host_port = cfg["telnet"].split(":")
|
||||
response = who(host_port[0], host_port[1], cfg["mycallsign"])
|
||||
return response
|
||||
|
||||
@app.route('/', methods=['GET'])
|
||||
@app.route('/index.html', methods=['GET'])
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
@app.route("/index.html", methods=["GET"])
|
||||
def spots():
|
||||
# payload=spotquery()
|
||||
response=flask.Response(render_template('index.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list'],enable_cq_filter=enable_cq_filter,timer_interval=cfg['timer']['interval'],adxo_events=adxo_events,continents=continents_cq,bands=band_frequencies))
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"index.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
enable_cq_filter=enable_cq_filter,
|
||||
timer_interval=cfg["timer"]["interval"],
|
||||
adxo_events=adxo_events,
|
||||
continents=continents_cq,
|
||||
bands=band_frequencies,
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
@app.route('/service-worker.js', methods=['GET'])
|
||||
|
||||
@app.route("/service-worker.js", methods=["GET"])
|
||||
def sw():
|
||||
return app.send_static_file('service-worker.js')
|
||||
return app.send_static_file("service-worker.js")
|
||||
|
||||
@app.route('/offline.html')
|
||||
|
||||
@app.route("/offline.html")
|
||||
def root():
|
||||
return app.send_static_file('html/offline.html')
|
||||
return app.send_static_file("html/rel/offline.html")
|
||||
|
||||
@app.route('/world.json')
|
||||
|
||||
@app.route("/world.json")
|
||||
def world_data():
|
||||
return app.send_static_file('data/world.json')
|
||||
return app.send_static_file("data/world.json")
|
||||
|
||||
@app.route('/plots.html')
|
||||
|
||||
@app.route("/plots.html")
|
||||
def plots():
|
||||
whoj = who_is_connected()
|
||||
response=flask.Response(render_template('plots.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list'],who=whoj,continents=continents_cq,bands=band_frequencies))
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"plots.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
who=whoj,
|
||||
continents=continents_cq,
|
||||
bands=band_frequencies,
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/cookies.html', methods=['GET'])
|
||||
@app.route("/cookies.html", methods=["GET"])
|
||||
def cookies():
|
||||
response=flask.Response(render_template('cookies.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list']))
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"cookies.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
@app.route('/privacy.html', methods=['GET'])
|
||||
|
||||
@app.route("/privacy.html", methods=["GET"])
|
||||
def privacy():
|
||||
response=flask.Response(render_template('privacy.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list']))
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"privacy.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
@app.route('/sitemap.xml')
|
||||
def sitemap():
|
||||
return app.send_static_file('sitemap.xml')
|
||||
|
||||
@app.route('/callsign.html', methods=['GET'])
|
||||
@app.route("/sitemap.xml")
|
||||
def sitemap():
|
||||
return app.send_static_file("sitemap.xml")
|
||||
|
||||
|
||||
@app.route("/callsign.html", methods=["GET"])
|
||||
def callsign():
|
||||
# payload=spotquery()
|
||||
callsign=request.args.get('c')
|
||||
response=flask.Response(render_template('callsign.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list'],timer_interval=cfg['timer']['interval'],callsign=callsign,adxo_events=adxo_events,continents=continents_cq,bands=band_frequencies))
|
||||
callsign = request.args.get("c")
|
||||
response = flask.Response(
|
||||
render_template(
|
||||
"callsign.html",
|
||||
mycallsign=cfg["mycallsign"],
|
||||
telnet=cfg["telnet"],
|
||||
mail=cfg["mail"],
|
||||
menu_list=cfg["menu"]["menu_list"],
|
||||
timer_interval=cfg["timer"]["interval"],
|
||||
callsign=callsign,
|
||||
adxo_events=adxo_events,
|
||||
continents=continents_cq,
|
||||
bands=band_frequencies,
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
# API that search a callsign and return all informations about that
|
||||
@app.route('/callsign', methods=['GET'])
|
||||
@app.route("/callsign", methods=["GET"])
|
||||
def find_callsign():
|
||||
callsign=request.args.get('c')
|
||||
callsign = request.args.get("c")
|
||||
response = pfxt.find(callsign)
|
||||
if response is None:
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_heatmap_data', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_heatmap_data", methods=["GET"])
|
||||
def get_heatmap_data():
|
||||
continent=request.args.get('continent')
|
||||
continent = request.args.get("continent")
|
||||
response = flask.Response(json.dumps(heatmap_cbp.get_data(continent)))
|
||||
logger.debug(response)
|
||||
if response is None:
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_dx_spots_per_month', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_dx_spots_per_month", methods=["GET"])
|
||||
def get_dx_spots_per_month():
|
||||
response = flask.Response(json.dumps(bar_graph_spm.get_data()))
|
||||
logger.debug(response)
|
||||
@ -325,7 +416,8 @@ def get_dx_spots_per_month():
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_dx_spots_trend', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_dx_spots_trend", methods=["GET"])
|
||||
def get_dx_spots_trend():
|
||||
response = flask.Response(json.dumps(line_graph_st.get_data()))
|
||||
logger.debug(response)
|
||||
@ -333,7 +425,8 @@ def get_dx_spots_trend():
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_hour_band', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_hour_band", methods=["GET"])
|
||||
def get_dx_hour_band():
|
||||
response = flask.Response(json.dumps(bubble_graph_hb.get_data()))
|
||||
logger.debug(response)
|
||||
@ -341,7 +434,8 @@ def get_dx_hour_band():
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
@app.route('/plot_get_world_dx_spots_live', methods=['GET'])
|
||||
|
||||
@app.route("/plot_get_world_dx_spots_live", methods=["GET"])
|
||||
def get_world_dx_spots_live():
|
||||
response = flask.Response(json.dumps(geo_graph_wdsl.get_data()))
|
||||
logger.debug(response)
|
||||
@ -349,26 +443,32 @@ def get_world_dx_spots_live():
|
||||
response = flask.Response(status=204)
|
||||
return response
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def inject_template_scope():
|
||||
injections = dict()
|
||||
|
||||
def cookies_check():
|
||||
value = request.cookies.get('cookie_consent')
|
||||
return value == 'true'
|
||||
value = request.cookies.get("cookie_consent")
|
||||
return value == "true"
|
||||
|
||||
injections.update(cookies_check=cookies_check)
|
||||
return injections
|
||||
|
||||
|
||||
@app.after_request
|
||||
def add_security_headers(resp):
|
||||
resp.headers['Strict-Transport-Security']='max-age=1000'
|
||||
resp.headers['X-Xss-Protection']='1; mode=block'
|
||||
resp.headers['X-Frame-Options']='SAMEORIGIN'
|
||||
resp.headers['X-Content-Type-Options']='nosniff'
|
||||
resp.headers['Referrer-Policy']='strict-origin-when-cross-origin'
|
||||
resp.headers['Cache-Control']='public, no-cache'
|
||||
resp.headers['Pragma']='no-cache'
|
||||
resp.headers["Strict-Transport-Security"] = "max-age=1000"
|
||||
resp.headers["X-Xss-Protection"] = "1; mode=block"
|
||||
resp.headers["X-Frame-Options"] = "SAMEORIGIN"
|
||||
resp.headers["X-Content-Type-Options"] = "nosniff"
|
||||
resp.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
||||
resp.headers["Cache-Control"] = "public, no-cache"
|
||||
resp.headers["Pragma"] = "no-cache"
|
||||
|
||||
resp.headers['Content-Security-Policy']="\
|
||||
resp.headers[
|
||||
"Content-Security-Policy"
|
||||
] = "\
|
||||
default-src 'self';\
|
||||
script-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
||||
style-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
||||
@ -384,10 +484,10 @@ def add_security_headers(resp):
|
||||
"
|
||||
return resp
|
||||
|
||||
|
||||
# style-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx' 'sha512-uvXdJud8WaOlQFjlz9B15Yy2Au/bMAvz79F7Xa6OakCl2jvQPdHD0hb3dEqZRdSwG4/sknePXlE7GiarwA/9Wg==';\
|
||||
# style-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline';\
|
||||
|
||||
# script-src 'self' cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline'
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0')
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0")
|
||||
|
Loading…
Reference in New Issue
Block a user