2020-03-08 07:48:40 +00:00
|
|
|
import os
|
2020-01-22 15:18:38 +00:00
|
|
|
import flask
|
2020-03-08 07:48:40 +00:00
|
|
|
from flask import request, render_template, jsonify
|
2021-11-28 17:31:32 +00:00
|
|
|
from flask_wtf.csrf import CSRFProtect
|
|
|
|
from flask_minify import minify
|
2020-01-22 15:18:38 +00:00
|
|
|
import json
|
2021-05-06 14:54:58 +00:00
|
|
|
import time, threading
|
|
|
|
import logging
|
|
|
|
import logging.config
|
2021-05-07 05:52:09 +00:00
|
|
|
from lib.dxtelnet import who
|
|
|
|
from lib.adxo import get_adxo_events
|
2021-05-25 10:12:29 +00:00
|
|
|
from lib.qry import query_manager
|
2021-12-12 14:34:59 +00:00
|
|
|
from lib.cty import prefix_table
|
2020-02-01 06:12:53 +00:00
|
|
|
__author__ = 'IU1BOW - Corrado'
|
|
|
|
|
2021-11-28 17:31:32 +00:00
|
|
|
|
2021-05-06 14:54:58 +00:00
|
|
|
logging.config.fileConfig("cfg/webapp_log_config.ini", disable_existing_loggers=True)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
logger.info("Start")
|
|
|
|
|
2020-01-22 15:18:38 +00:00
|
|
|
app = flask.Flask(__name__)
|
2020-11-08 10:44:50 +00:00
|
|
|
app.config["DEBUG"] = False
|
2020-02-01 06:12:53 +00:00
|
|
|
app.config['SECRET_KEY'] = 'secret!'
|
2021-11-28 17:31:32 +00:00
|
|
|
app.config['WTF_CSRF_SECRET_KEY']='wtfsecret!'
|
|
|
|
app.config.update(
|
|
|
|
SESSION_COOKIE_SECURE=True,
|
|
|
|
SESSION_COOKIE_HTTPONLY=True,
|
2021-12-04 06:24:26 +00:00
|
|
|
SESSION_COOKIE_SAMESITE='Strict',
|
2021-11-28 17:31:32 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
csrf = CSRFProtect(app)
|
|
|
|
minify(app=app, html=True, js=True,cssless=False)
|
|
|
|
#minify(app=app, html=False, js=False,cssless=False)
|
2020-01-22 15:18:38 +00:00
|
|
|
|
2021-05-06 14:54:58 +00:00
|
|
|
|
2020-03-08 07:48:40 +00:00
|
|
|
#load config file
|
|
|
|
with open('cfg/config.json') as json_data_file:
|
2020-01-22 16:24:20 +00:00
|
|
|
cfg = json.load(json_data_file)
|
|
|
|
|
2020-03-08 07:48:40 +00:00
|
|
|
#load bands file
|
|
|
|
with open('cfg/bands.json') as json_bands:
|
|
|
|
band_frequencies = json.load(json_bands)
|
|
|
|
|
2020-10-17 04:36:07 +00:00
|
|
|
#load mode file
|
|
|
|
with open('cfg/modes.json') as json_modes:
|
|
|
|
modes_frequencies = json.load(json_modes)
|
|
|
|
|
2020-03-08 15:04:02 +00:00
|
|
|
#load continents-cq file
|
|
|
|
with open('cfg/continents.json') as json_continents:
|
|
|
|
continents_cq = json.load(json_continents)
|
|
|
|
|
2021-12-12 14:34:59 +00:00
|
|
|
#read and set default for enabling cq filter
|
|
|
|
if cfg.get('enable_cq_filter'):
|
|
|
|
enable_cq_filter=cfg['enable_cq_filter'].upper()
|
|
|
|
else:
|
|
|
|
enable_cq_filter='N'
|
|
|
|
|
|
|
|
|
|
|
|
#define country table for search info on callsigns
|
|
|
|
pfxt=prefix_table()
|
|
|
|
|
|
|
|
|
2021-05-25 10:12:29 +00:00
|
|
|
#create object query manager
|
|
|
|
qm=query_manager()
|
|
|
|
|
2020-03-08 15:04:02 +00:00
|
|
|
#find id in json : ie frequency / continent
|
|
|
|
def find_id_json(json_object, name):
|
2020-03-08 07:48:40 +00:00
|
|
|
return [obj for obj in json_object if obj['id']==name][0]
|
|
|
|
|
2020-09-20 05:10:13 +00:00
|
|
|
#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
|
2020-02-01 06:12:53 +00:00
|
|
|
def spotquery():
|
2020-01-22 15:18:38 +00:00
|
|
|
|
2020-03-08 07:48:40 +00:00
|
|
|
try:
|
|
|
|
#get url parameters
|
2021-12-12 14:34:59 +00:00
|
|
|
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
|
|
|
|
deitu=(request.args.getlist('ie')) #DE ITU zone filter
|
|
|
|
dxitu=(request.args.getlist('ix')) #DX ITU zone filter
|
|
|
|
callsign=request.args.get('c') #search specific callsign
|
|
|
|
|
2020-10-25 18:07:35 +00:00
|
|
|
|
2020-09-20 05:10:13 +00:00
|
|
|
query_string=''
|
|
|
|
if callsign:
|
|
|
|
#construct the query, to show last 6 month
|
2021-05-25 10:12:29 +00:00
|
|
|
if len(callsign)<=14:
|
|
|
|
query_string="(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotter='"+callsign+"'"
|
|
|
|
query_string+=" ORDER BY rowid desc limit 10)"
|
|
|
|
query_string+=" UNION "
|
|
|
|
query_string+="(SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE spotcall='"+callsign+"'"
|
|
|
|
query_string+=" ORDER BY rowid desc limit 10);"
|
|
|
|
else:
|
|
|
|
logging.warning('callsign too long')
|
2020-09-20 05:10:13 +00:00
|
|
|
|
|
|
|
else:
|
2020-10-17 04:36:07 +00:00
|
|
|
#construct band query decoding frequencies with json file
|
2020-09-20 05:10:13 +00:00
|
|
|
band_qry_string = ' AND (('
|
|
|
|
for i in range(len(band)):
|
|
|
|
freq=find_id_json(band_frequencies["bands"],band[i])
|
|
|
|
if i > 0:
|
|
|
|
band_qry_string += ') OR ('
|
|
|
|
|
|
|
|
band_qry_string += 'freq BETWEEN ' + str(freq["min"]) + ' AND ' + str(freq["max"])
|
|
|
|
|
|
|
|
band_qry_string += '))'
|
|
|
|
|
2020-10-17 04:36:07 +00:00
|
|
|
#construct mode query
|
|
|
|
mode_qry_string = ' AND (('
|
|
|
|
for i in range(len(mode)):
|
|
|
|
single_mode=find_id_json(modes_frequencies["modes"],mode[i])
|
|
|
|
if i > 0:
|
|
|
|
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 += '))'
|
|
|
|
|
2020-09-20 05:10:13 +00:00
|
|
|
#construct DE continent region query
|
|
|
|
dere_qry_string = ' AND spottercq IN ('
|
|
|
|
for i in range(len(dere)):
|
|
|
|
continent=find_id_json(continents_cq["continents"],dere[i])
|
|
|
|
if i > 0:
|
|
|
|
dere_qry_string +=','
|
|
|
|
dere_qry_string += str(continent["cq"])
|
|
|
|
dere_qry_string +=')'
|
2020-03-08 15:04:02 +00:00
|
|
|
|
2020-09-20 05:10:13 +00:00
|
|
|
#construct DX continent region query
|
|
|
|
dxre_qry_string = ' AND spotcq IN ('
|
|
|
|
for i in range(len(dxre)):
|
|
|
|
continent=find_id_json(continents_cq["continents"],dxre[i])
|
|
|
|
if i > 0:
|
|
|
|
dxre_qry_string +=','
|
|
|
|
dxre_qry_string += str(continent["cq"])
|
|
|
|
dxre_qry_string +=')'
|
|
|
|
|
2021-12-12 14:34:59 +00:00
|
|
|
#construct de cq query
|
|
|
|
decq_qry_string = ''
|
|
|
|
if len(decq)==1:
|
|
|
|
if decq[0].isnumeric():
|
|
|
|
decq_qry_string = ' AND spottercq =' + decq[0]
|
|
|
|
|
|
|
|
#construct dx cq query
|
|
|
|
dxcq_qry_string = ''
|
|
|
|
if len(dxcq)==1:
|
|
|
|
if dxcq[0].isnumeric():
|
|
|
|
dxcq_qry_string = ' AND spotcq =' + dxcq[0]
|
|
|
|
|
|
|
|
#construct de itu query
|
|
|
|
deitu_qry_string = ''
|
|
|
|
if len(deitu)==1:
|
|
|
|
if deitu[0].isnumeric():
|
|
|
|
deitu_qry_string = ' AND spotteritu =' + deitu[0]
|
|
|
|
|
|
|
|
#construct dx itu query
|
|
|
|
dxitu_qry_string = ''
|
|
|
|
if len(dxitu)==1:
|
|
|
|
if dxitu[0].isnumeric():
|
|
|
|
dxitu_qry_string = ' AND spotitu =' + dxitu[0]
|
|
|
|
|
2020-10-17 04:36:07 +00:00
|
|
|
|
2020-09-20 05:10:13 +00:00
|
|
|
query_string="SELECT rowid, spotter AS de, freq, spotcall AS dx, comment AS comm, time, spotdxcc from dxcluster.spot WHERE 1=1"
|
|
|
|
if len(band) > 0:
|
|
|
|
query_string += band_qry_string
|
|
|
|
|
2020-10-17 04:36:07 +00:00
|
|
|
if len(mode) > 0:
|
|
|
|
query_string += mode_qry_string
|
|
|
|
|
2020-09-20 05:10:13 +00:00
|
|
|
if len(dere) > 0:
|
|
|
|
query_string += dere_qry_string
|
|
|
|
|
|
|
|
if len(dxre) > 0:
|
|
|
|
query_string += dxre_qry_string
|
2021-05-25 10:12:29 +00:00
|
|
|
|
2021-12-12 14:34:59 +00:00
|
|
|
if len(decq_qry_string) > 0:
|
|
|
|
query_string += decq_qry_string
|
|
|
|
|
|
|
|
if len(dxcq_qry_string) > 0:
|
|
|
|
query_string += dxcq_qry_string
|
|
|
|
|
|
|
|
if len(deitu_qry_string) > 0:
|
|
|
|
query_string += deitu_qry_string
|
|
|
|
|
|
|
|
if len(dxitu_qry_string) > 0:
|
|
|
|
query_string += dxitu_qry_string
|
|
|
|
|
2020-09-20 05:10:13 +00:00
|
|
|
query_string += " ORDER BY rowid desc limit 50;"
|
2020-03-08 07:48:40 +00:00
|
|
|
|
2021-05-25 10:12:29 +00:00
|
|
|
logger.debug(query_string)
|
|
|
|
qm.qry(query_string)
|
|
|
|
data=qm.get_data()
|
|
|
|
row_headers=qm.get_headers()
|
|
|
|
|
|
|
|
logger.debug("query done")
|
|
|
|
logger.debug (data)
|
|
|
|
|
|
|
|
if data is None or len(data)==0:
|
|
|
|
logger.warning("no data found")
|
|
|
|
|
2020-03-08 07:48:40 +00:00
|
|
|
payload=[]
|
2021-05-25 10:12:29 +00:00
|
|
|
for result in data:
|
2021-12-12 14:34:59 +00:00
|
|
|
# create dictionary from recorset
|
|
|
|
main_result=dict(zip(row_headers,result))
|
|
|
|
# find the country in prefix table
|
|
|
|
search_prefix=pfxt.find(main_result["dx"])
|
|
|
|
# merge recordset and contry prefix
|
|
|
|
payload.append({**main_result, **search_prefix})
|
2020-03-08 07:48:40 +00:00
|
|
|
return payload
|
|
|
|
except Exception as e:
|
2021-05-06 14:54:58 +00:00
|
|
|
logger.error(e)
|
2020-03-08 07:48:40 +00:00
|
|
|
|
2021-05-06 14:54:58 +00:00
|
|
|
#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()
|
|
|
|
|
2020-02-01 06:12:53 +00:00
|
|
|
@app.route('/spotlist', methods=['GET'])
|
|
|
|
def spotlist():
|
2020-02-08 09:14:30 +00:00
|
|
|
response=flask.Response(json.dumps(spotquery()))
|
|
|
|
return response
|
2020-02-01 06:12:53 +00:00
|
|
|
|
2020-11-08 10:44:50 +00:00
|
|
|
def who_is_connected():
|
|
|
|
host_port=cfg['telnet'].split(':')
|
|
|
|
response=who(host_port[0],host_port[1],cfg['mycallsign'])
|
|
|
|
return response
|
|
|
|
|
2020-02-08 09:14:30 +00:00
|
|
|
@app.route('/', methods=['GET'])
|
2020-03-14 16:48:56 +00:00
|
|
|
@app.route('/index.html', methods=['GET'])
|
2020-02-08 09:14:30 +00:00
|
|
|
def spots():
|
2020-02-01 06:12:53 +00:00
|
|
|
payload=spotquery()
|
2021-12-12 14:34:59 +00:00
|
|
|
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,payload=payload,timer_interval=cfg['timer']['interval'],adxo_events=adxo_events))
|
2020-02-08 09:14:30 +00:00
|
|
|
return response
|
2020-01-22 15:18:38 +00:00
|
|
|
|
2020-03-14 16:48:56 +00:00
|
|
|
@app.route('/service-worker.js', methods=['GET'])
|
|
|
|
def sw():
|
|
|
|
return app.send_static_file('service-worker.js')
|
|
|
|
|
|
|
|
@app.route('/offline.html')
|
|
|
|
def root():
|
|
|
|
return app.send_static_file('html/offline.html')
|
|
|
|
|
2020-06-16 08:17:07 +00:00
|
|
|
@app.route('/plotlist', methods=['GET'])
|
|
|
|
def plotlist():
|
|
|
|
#get url parameters
|
|
|
|
idxfile=os.path.join(app.root_path,os.path.basename(app.static_url_path),'plots','plots.json')
|
|
|
|
if os.path.exists(idxfile):
|
|
|
|
with open(idxfile,'r') as jsonfile:
|
|
|
|
json_content = json.load(jsonfile)
|
|
|
|
else:
|
|
|
|
json_content={}
|
|
|
|
|
|
|
|
response=json_content
|
|
|
|
return response
|
|
|
|
|
|
|
|
@app.route('/plots.html')
|
|
|
|
def plots():
|
|
|
|
payload=plotlist()
|
2020-11-08 10:44:50 +00:00
|
|
|
whoj=who_is_connected()
|
2021-11-18 16:41:49 +00:00
|
|
|
response=flask.Response(render_template('plots.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list'],payload=payload,timer_interval=cfg['plot_refresh_timer']['interval'],who=whoj))
|
2020-06-16 08:17:07 +00:00
|
|
|
return response
|
|
|
|
|
|
|
|
|
2020-09-26 16:07:19 +00:00
|
|
|
@app.route('/cookies.html', methods=['GET'])
|
2020-06-16 08:17:07 +00:00
|
|
|
def cookies():
|
2021-11-18 16:41:49 +00:00
|
|
|
response=flask.Response(render_template('cookies.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list']))
|
2020-09-26 16:07:19 +00:00
|
|
|
return response
|
2020-06-16 08:17:07 +00:00
|
|
|
|
2020-11-08 10:44:50 +00:00
|
|
|
@app.route('/privacy.html', methods=['GET'])
|
|
|
|
def privacy():
|
2021-11-18 16:41:49 +00:00
|
|
|
response=flask.Response(render_template('privacy.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list']))
|
2020-11-08 10:44:50 +00:00
|
|
|
return response
|
|
|
|
|
2020-03-14 16:48:56 +00:00
|
|
|
@app.route('/sitemap.xml')
|
|
|
|
def sitemap():
|
|
|
|
return app.send_static_file('sitemap.xml')
|
|
|
|
|
2020-09-20 05:10:13 +00:00
|
|
|
@app.route('/callsign.html', methods=['GET'])
|
|
|
|
def callsign():
|
|
|
|
payload=spotquery()
|
2021-12-12 14:34:59 +00:00
|
|
|
#country_data=load_country()
|
|
|
|
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'],payload=payload,timer_interval=cfg['timer']['interval'],country_data=country_data,callsign=callsign,adxo_events=adxo_events))
|
|
|
|
response=flask.Response(render_template('callsign.html',mycallsign=cfg['mycallsign'],telnet=cfg['telnet'],mail=cfg['mail'],menu_list=cfg['menu']['menu_list'],payload=payload,timer_interval=cfg['timer']['interval'],callsign=callsign,adxo_events=adxo_events))
|
|
|
|
return response
|
|
|
|
|
|
|
|
#API that search a callsign and return all informations about that
|
|
|
|
@app.route('/callsign', methods=['GET'])
|
|
|
|
def find_callsign():
|
2020-09-20 05:10:13 +00:00
|
|
|
callsign=request.args.get('c')
|
2021-12-12 14:34:59 +00:00
|
|
|
response=pfxt.find(callsign)
|
|
|
|
if response is None:
|
|
|
|
response=flask.Response(status=204)
|
2020-09-20 05:10:13 +00:00
|
|
|
return response
|
2020-11-08 10:44:50 +00:00
|
|
|
|
2021-11-28 17:31:32 +00:00
|
|
|
@app.after_request
|
|
|
|
def add_security_headers(resp):
|
|
|
|
# resp.headers['Content-Security-Policy']='script-src \'self\' cdnjs.cloudflare.com cdn.jsdelivr.net \'unsafe-inline\''
|
|
|
|
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']='no-store, max-age=0'
|
|
|
|
resp.headers['Cache-Control']='no-cache, no-store, must-revalidate'
|
|
|
|
resp.headers['Pragma']='no-cache'
|
|
|
|
# resp.headers['Access-Control-Allow-Origin']='https://cdnjs.cloudflare.com'
|
|
|
|
return resp
|
|
|
|
|
2020-06-16 08:17:07 +00:00
|
|
|
|
2020-02-01 06:12:53 +00:00
|
|
|
if __name__ == '__main__':
|
2020-02-08 09:14:30 +00:00
|
|
|
app.run(host='0.0.0.0')
|