Skip to content

Commit

Permalink
Incorporate sota summits into the parks table of the db.
Browse files Browse the repository at this point in the history
No schema changes needed. Unused pota fields used for some sota fields.
Meta-data for spots and hunts for summits now work as expected.
- Spotting a SOTA qso is not enabled.
  • Loading branch information
cwhelchel committed Sep 25, 2024
1 parent f335596 commit 4bcd912
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 40 deletions.
4 changes: 4 additions & 0 deletions src/@types/Parks.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export interface Park {
parktypeId: number;
parktypeDesc: string;
locationDesc: string;
locationName: string;
entityName: string;
accessMethods: string;
activationMethods: string;
firstActivator: string;
firstActivationDate: string;
website: string;
Expand Down
48 changes: 45 additions & 3 deletions src/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,40 @@ def get_park(self, ref: str, pull_from_pota: bool = True) -> str:
ps = ParkSchema()
return ps.dumps(park)

def get_summit(self, ref: str, pull_from_sota: bool = True) -> str:
'''
Returns the JSON for the summit (same schema as park) if in the db
:param str ref: the SOTA summit reference string
:param bool pull_from_pota: True (default) to force API query for
summit
:returns JSON of park object in db or None if not found
'''
if ref is None:
logging.error("get_summit: ref param was None")
return

logging.debug(f"get_park: getting summit {ref}")

summit = self.db.parks.get_park(ref)

if summit is None and pull_from_sota:
api_res = self.sota.get_summit(ref)
logging.debug(f"get_park: summit pulled from api {api_res}")
self.db.parks.update_summit_data(api_res)
summit = self.db.parks.get_park(ref)
# we dont import any SOTA qsos yet so not needed
# elif summit.name is None:
# logging.debug(f"get_park: park Name was None {ref}")
# api_res = self.pota.get_park(ref)
# logging.debug(f"get_park: park from api {api_res}")
# self.db.parks.update_park_data(api_res)
# summit = self.db.parks.get_park(ref)

ps = ParkSchema()
return ps.dumps(summit)

def get_park_hunts(self, ref: str) -> str:
'''
Returns a JSON object containing the number of QSOs with activators at
Expand Down Expand Up @@ -261,9 +295,17 @@ def log_qso(self, qso_data, update_spots: bool = True):
cfg = self.db.get_user_config()

try:
park_json = self.pota.get_park(qso_data['sig_info'])
logging.debug(f"updating park stat for: {park_json}")
self.db.parks.inc_park_hunt(park_json)
if qso_data['sig'] == 'POTA':
park_json = self.pota.get_park(qso_data['sig_info'])
logging.debug(f"updating park stat for: {park_json}")
self.db.parks.inc_park_hunt(park_json)
elif qso_data['sig'] == 'SOTA':
summit_code = qso_data['sig_info']
ok = self.db.parks.inc_summit_hunt(summit_code)
if not ok:
summit = self.sota.get_summit(summit_code)
self.db.parks.update_summit_data(summit)
self.db.parks.inc_summit_hunt(summit_code)

qso_data['tx_pwr'] = cfg.default_pwr
logging.debug(f"logging qso: {qso_data}")
Expand Down
35 changes: 21 additions & 14 deletions src/components/Map/ParkInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ export default function ParkInfo() {
let park = contextData?.park?.reference || '';
if (park === null || park === '')
return;
getParkStats(park).then((x: ParkStats) => {
setStats(x);
});

if (contextData?.park?.parktypeId != 0) {
getParkStats(park).then((x: ParkStats) => {
setStats(x);
});
}

window.pywebview.api.get_park_hunts(park).then((j: string) => {
let o = checkApiResponse(j, contextData, setData);
Expand All @@ -34,27 +37,28 @@ export default function ParkInfo() {
return (
<div id="parkInfo">
<div id="parkTitleContainer">
{contextData && contextData?.park && (
{contextData && contextData?.park && contextData?.park?.parktypeId != 0 && (
parkTitle()
)}
{contextData && contextData?.summit && (
{contextData && contextData?.park?.parktypeDesc == 'SOTA SUMMIT' && (
summitTitle()
)}
<hr role='separator' className='sep' />
</div>
<LeafMap />
<div id="parkStatsContainer">
{stats && contextData?.park && (
{stats && contextData?.park && contextData?.park?.parktypeId != 0 && (
<>
{parkStats()} <br />
{firstActivator()} <br />
{locationDesc()} <br />
{parkHunts()}
</>
)}
{contextData?.summit && (
{contextData?.park?.parktypeDesc == 'SOTA SUMMIT' && (
<>
{summitInfo()} <br />
{parkHunts()}
</>
)}
</div>
Expand Down Expand Up @@ -90,18 +94,21 @@ export default function ParkInfo() {
}

function summitTitle(): any {
//const url = `https://pota.app/#/park/${contextData?.park?.reference}`;
const text = `${contextData?.summit?.summitCode} - ${contextData?.summit?.name}`;
const url = `${contextData?.park?.website}`;
const text = `🗻 ${contextData?.park?.reference} - ${contextData?.park?.name}`;

return <span id="parkTitle">{text}</span>;
return <span id="parkTitle" onClick={() => {
window.open(url);
}}>{text}</span>;
}

function summitInfo(): React.ReactNode {
// for random summit info we hijack some of the not-displayed pieces of park info
return <>
<span>region: {contextData?.summit?.regionName}</span> <br/>
<span>assn: {contextData?.summit?.associationName} - {contextData?.summit?.associationCode}</span> <br/>
<span>points: {contextData?.summit?.points}</span> <br/>
<span>alt: {contextData?.summit?.altFt} ft - {contextData?.summit?.altM} m</span>
<span>region: {contextData?.park?.locationName}</span> <br />
<span>entity: {contextData?.park?.entityName}</span> <br />
<span>points: {contextData?.park?.accessMethods}</span> <br />
<span>alt: {contextData?.park?.activationMethods}</span> <br />
</>;
}
}
8 changes: 7 additions & 1 deletion src/components/QsoEntry/QsoEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ export default function QsoEntry() {
}

function spotActivator() {
if (qso.sig != 'POTA')
return;
console.log(`spotting activator at ${contextData.park?.name}`);
let park = qso.sig_info;

Expand Down Expand Up @@ -230,7 +232,11 @@ export default function QsoEntry() {
locationDesc: apiData.locationDesc,
firstActivator: apiData.firstActivator,
firstActivationDate: apiData.firstActivationDate,
website: ''
website: '',
locationName: '',
entityName: '',
accessMethods: '',
activationMethods: ''
};

newCtxData.park = x;
Expand Down
30 changes: 21 additions & 9 deletions src/components/SpotViewer/SpotViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,15 +233,27 @@ export default function SpotViewer() {
});
});
} else if (x.sig == 'SOTA') {
getSummitInfo(x.sig_info).then((summit: Summit) => {
console.log("got summit: " + summit.summitCode);
const newCtxData = { ...contextData };
newCtxData.spotId = id;
newCtxData.qso = x;
newCtxData.summit = summit;
newCtxData.park = null;
setData(newCtxData);
});

window.pywebview.api.get_summit(x.sig_info)
.then((r: string) => {
let summit = JSON.parse(r) as Park;
const newCtxData = { ...contextData };
newCtxData.spotId = id;
newCtxData.qso = x;
//newCtxData.summit = summit;
newCtxData.park = summit;
setData(newCtxData);
});

// getSummitInfo(x.sig_info).then((summit: Summit) => {
// console.log("got summit: " + summit.summitCode);
// const newCtxData = { ...contextData };
// newCtxData.spotId = id;
// newCtxData.qso = x;
// newCtxData.summit = summit;
// newCtxData.park = null;
// setData(newCtxData);
// });
}

});
Expand Down
29 changes: 17 additions & 12 deletions src/db/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,7 @@ def update_all_spots(self, spots_json, sota_spots):
:param dict spots_json: the dict from the pota api
:param dict sota_spots: the dict from the sota api
'''
schema = SpotSchema()
self.session.execute(sa.text('DELETE FROM spots;'))
self.session.execute(sa.text('DELETE FROM comments;'))

# self._sq.insert_test_spot() # testing code

for s in spots_json:
to_add: Spot = schema.load(s, session=self.session)
to_add.spot_source = 'POTA'
self.session.add(to_add)

# get meta data for this spot
def get_spot_metadata(to_add: Spot):
park = self.parks.get_park(to_add.reference)
if park is not None and park.hunts > 0:
to_add.park_hunts = park.hunts
Expand All @@ -179,6 +168,20 @@ def update_all_spots(self, spots_json, sota_spots):
to_add.hunted = hunted
to_add.hunted_bands = bands

schema = SpotSchema()
self.session.execute(sa.text('DELETE FROM spots;'))
self.session.execute(sa.text('DELETE FROM comments;'))

# self._sq.insert_test_spot() # testing code

for s in spots_json:
to_add: Spot = schema.load(s, session=self.session)
to_add.spot_source = 'POTA'
self.session.add(to_add)

# get meta data for this spot
get_spot_metadata(to_add)

# sometimes locationDesc can be None. see GR-0071
if to_add.locationDesc is not None \
and ',' not in to_add.locationDesc:
Expand Down Expand Up @@ -214,6 +217,8 @@ def update_all_spots(self, spots_json, sota_spots):
else:
self.session.add(sota_to_add)

get_spot_metadata(sota_to_add)

self.session.commit()

def update_activator_stat(self, activator_stat_json) -> int:
Expand Down
64 changes: 63 additions & 1 deletion src/db/park_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,58 @@ def update_park_data(self, park: any, delay_commit: bool = False):
p.entityDeleted = park['entityDeleted']
p.firstActivator = park['firstActivator']
p.firstActivationDate = park['firstActivationDate']
p.firstActivationDate = park['firstActivationDate']
p.website = park['website']

if not delay_commit:
self.session.commit()

def update_summit_data(self, summit: any, delay_commit: bool = False):
'''
Update or insert a "park" with info from SOTA api for a summit
:param any summit: the json for a SOTA summit returned from SOTA api
:param bool delay_commit: true to not commit the session
'''
if summit is None:
return

p = self.get_park(summit['summitCode'])

if p is None:
logging.debug(f"inserting new {summit['summitCode']}")
to_add = Park()
to_add.reference = summit['summitCode']
to_add.name = summit['name']
to_add.grid4 = summit['locator'][:4]
to_add.grid6 = summit['locator']
to_add.active = 1 if bool(summit['valid']) else 0
to_add.latitude = summit['latitude']
to_add.longitude = summit['longitude']
to_add.parkComments = summit['notes']
to_add.accessibility = ''
to_add.sensitivity = ''
to_add.accessMethods = f"{summit['points']}"
to_add.activationMethods = f"{summit['altM']} m - {summit['altFt']} ft" # noqa E501
to_add.agencies = ''
to_add.agencyURLs = ''
to_add.parkURLs = ''
to_add.parktypeId = 0
to_add.parktypeDesc = 'SOTA SUMMIT'
to_add.locationDesc = summit['regionCode']
to_add.locationName = summit['regionName']
to_add.entityId = 0
to_add.entityName = summit['associationName']
to_add.referencePrefix = summit['regionCode']
to_add.entityDeleted = 0
to_add.firstActivator = ''
to_add.firstActivationDate = ''
to_add.website = f"https://www.sotadata.org.uk/en/summit/{summit['summitCode']}" # noqa E501

self.session.add(to_add)

if not delay_commit:
self.session.commit()

def inc_park_hunt(self, park: any):
'''
Increment the hunt count of a park by one. If park is not in db add it.
Expand All @@ -108,6 +154,22 @@ def inc_park_hunt(self, park: any):

self.session.commit()

def inc_summit_hunt(self, summit_ref: str) -> bool:
'''
Increment the hunt count of a summit "park" by one.
:param string summit_ref: the summit code of the "park"
:returns true if a summit "park" was found and updated.
'''
p = self.get_park(summit_ref)

if p is None:
return False

p.hunts += 1
self.session.commit()
return True

def update_park_hunts(self, park: any, hunts: int,
delay_commit: bool = True):
'''
Expand Down
1 change: 1 addition & 0 deletions src/sota/sota.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

logging = L.getLogger("sotaApi")

# -1 gets last hour of spots
SPOT_URL = "https://api2.sota.org.uk/api/spots/-1/all"
SUMMIT_URL = "https://api2.sota.org.uk/api/summits/"

Expand Down

0 comments on commit 4bcd912

Please sign in to comment.