Compare commits
No commits in common. "c5c0081976157651b799efc793c576331e36b497" and "b551a09b7e1b4124084176f896e239ae2f21c11a" have entirely different histories.
c5c0081976
...
b551a09b7e
5 changed files with 45 additions and 6343 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
# ignore our binary Excel file
|
|
||||||
*.xlsx
|
|
||||||
|
|
@ -22,7 +22,7 @@ pd.set_option("display.max_rows", 150)
|
||||||
|
|
||||||
|
|
||||||
# point to our various input files - adjust this to your environment
|
# point to our various input files - adjust this to your environment
|
||||||
data_dir = '/home/syncthing/ham-radio/ARES-RACES/Nets--PNW_ARES_DMR_Weekly_Net'
|
data_dir = '/home/ed/syncthing/ham-radio/ARES-RACES/Nets--PNW_ARES_DMR_Weekly_Net'
|
||||||
filename = 'Check_In_Data.xlsx'
|
filename = 'Check_In_Data.xlsx'
|
||||||
data_file = os.path.join(data_dir, filename)
|
data_file = os.path.join(data_dir, filename)
|
||||||
output_file = os.path.join(data_dir, "PNW_Digital_ARES_EMCOMM_Weekly_Net_Check_In_Form.txt")
|
output_file = os.path.join(data_dir, "PNW_Digital_ARES_EMCOMM_Weekly_Net_Check_In_Form.txt")
|
||||||
|
|
@ -88,18 +88,18 @@ checkins_df["Date"] = pd.to_datetime(checkins_df["Date"]).dt.strftime('%Y-%m-%d'
|
||||||
callinfo_df = pd.read_excel(xls, "Call_Data", dtype=str)
|
callinfo_df = pd.read_excel(xls, "Call_Data", dtype=str)
|
||||||
#print("callinfo_df:")
|
#print("callinfo_df:")
|
||||||
#print(callinfo_df)
|
#print(callinfo_df)
|
||||||
hoip_dir_df = pd.read_excel(xls, "hoip_subscribers", dtype=str)
|
hh_dir_df = pd.read_excel(xls, "Hamshack_Hotline", dtype=str)
|
||||||
#print("hoip_dir_df:")
|
#print("hh_dir_df:")
|
||||||
#print(hoip_dir_df)
|
#print(hh_dir_df)
|
||||||
|
|
||||||
# build dictionary of HH phone numbers
|
# build dictionary of HH phone numbers
|
||||||
hoip_phone_dict = {"Callsign" : "HH Number"}
|
hh_phone_dict = {"Callsign" : "HH Number"}
|
||||||
for i,row in hoip_dir_df.iterrows():
|
for i,row in hh_dir_df.iterrows():
|
||||||
callsign = row.iloc[1]
|
callsign = row.iloc[1]
|
||||||
hoip_num = row.iloc[0]
|
hh_num = row.iloc[7]
|
||||||
# we only add the hoip number first seen for a unique callsign
|
# we only add the hh number first seen for a unique callsign
|
||||||
if callsign not in hoip_phone_dict.keys():
|
if callsign not in hh_phone_dict.keys():
|
||||||
hoip_phone_dict.update({callsign : hoip_num})
|
hh_phone_dict.update({callsign : hh_num})
|
||||||
|
|
||||||
# fill in any missing data (NAN's) with default text
|
# fill in any missing data (NAN's) with default text
|
||||||
callinfo_df.fillna({'Name' : ''}, inplace=True)
|
callinfo_df.fillna({'Name' : ''}, inplace=True)
|
||||||
|
|
@ -127,27 +127,27 @@ for i,row in checkins_df.iterrows():
|
||||||
# build dictionary of call info indexed by callsign
|
# build dictionary of call info indexed by callsign
|
||||||
call_data_dict = {}
|
call_data_dict = {}
|
||||||
for i,row in callinfo_df.iterrows():
|
for i,row in callinfo_df.iterrows():
|
||||||
# row of data is call, name, state, district, county, affiliation, hoip_num
|
# row of data is call, name, state, district, county, affiliation, hh_num
|
||||||
# dictionary becomes: { call: [name[0], state[1], district[2], county[3], affiliation[4]], hoip_num[5]}
|
# dictionary becomes: { call: [name[0], state[1], district[2], county[3], affiliation[4]], hh_num[5]}
|
||||||
callsign = row.iloc[0]
|
callsign = row.iloc[0]
|
||||||
if callsign in hoip_phone_dict.keys():
|
if callsign in hh_phone_dict.keys():
|
||||||
hoip_num = hoip_phone_dict[callsign]
|
hh_num = hh_phone_dict[callsign]
|
||||||
else:
|
else:
|
||||||
hoip_num = ""
|
hh_num = ""
|
||||||
call_data_dict.update({callsign:[row.iloc[1],row.iloc[2],row.iloc[3],row.iloc[4],row.iloc[5],hoip_num]})
|
call_data_dict.update({callsign:[row.iloc[1],row.iloc[2],row.iloc[3],row.iloc[4],row.iloc[5],hh_num]})
|
||||||
|
|
||||||
# build checkin_form_dict dictionary
|
# build checkin_form_dict dictionary
|
||||||
checkin_form_dict = blank_checkin_form_dict
|
checkin_form_dict = blank_checkin_form_dict
|
||||||
for call in call_data_dict.keys():
|
for call in call_data_dict.keys():
|
||||||
#print("Looking at:", call)
|
#print("Looking at:", call)
|
||||||
#if call in hoip_phone_dict.keys():
|
#if call in hh_phone_dict.keys():
|
||||||
#print("Call "+call+" has HOIP #"+hoip_phone_dict[call])
|
#print("Call "+call+" has HH VOIP #"+hh_phone_dict[call])
|
||||||
call_state = call_data_dict[call][1]
|
call_state = call_data_dict[call][1]
|
||||||
call_dist = call_data_dict[call][2]
|
call_dist = call_data_dict[call][2]
|
||||||
call_county = call_data_dict[call][3]
|
call_county = call_data_dict[call][3]
|
||||||
call_affil = call_data_dict[call][4]
|
call_affil = call_data_dict[call][4]
|
||||||
call_hoip_num = call_data_dict[call][5]
|
call_hh_num = call_data_dict[call][5]
|
||||||
#print("State, Dist, Cnty, Affil, HH Num: ", call_state, call_dist, call_county, call_affil, call_hoip_num)
|
#print("State, Dist, Cnty, Affil, HH Num: ", call_state, call_dist, call_county, call_affil, call_hh_num)
|
||||||
|
|
||||||
# sanity check the data - error if not found in dictionary keys...
|
# sanity check the data - error if not found in dictionary keys...
|
||||||
# print("Verifying at: ", call, call_state, call_dist, call_county)
|
# print("Verifying at: ", call, call_state, call_dist, call_county)
|
||||||
|
|
@ -218,8 +218,8 @@ with open(output_file,"w") as outfile:
|
||||||
checkin_str = ""
|
checkin_str = ""
|
||||||
if checkin_count > 0:
|
if checkin_count > 0:
|
||||||
outfile.write(" "+call+", "+call_name+checkin_str)
|
outfile.write(" "+call+", "+call_name+checkin_str)
|
||||||
if call in hoip_phone_dict.keys():
|
if call in hh_phone_dict.keys():
|
||||||
outfile.write(", HOIP #"+hoip_phone_dict[call])
|
outfile.write(", HH VOIP #"+hh_phone_dict[call])
|
||||||
if call_affil != "":
|
if call_affil != "":
|
||||||
outfile.write(", "+call_affil+"\n")
|
outfile.write(", "+call_affil+"\n")
|
||||||
else:
|
else:
|
||||||
|
|
@ -237,8 +237,8 @@ with open(output_file,"w") as outfile:
|
||||||
checkin_str = ""
|
checkin_str = ""
|
||||||
if checkin_count > 0:
|
if checkin_count > 0:
|
||||||
outfile.write(" "+call+", "+call_data_dict[call][0]+checkin_str)
|
outfile.write(" "+call+", "+call_data_dict[call][0]+checkin_str)
|
||||||
if call in hoip_phone_dict.keys():
|
if call in hh_phone_dict.keys():
|
||||||
outfile.write(", HOIP #"+hoip_phone_dict[call])
|
outfile.write(", HH VOIP #"+hh_phone_dict[call])
|
||||||
affil = call_data_dict[call][4]
|
affil = call_data_dict[call][4]
|
||||||
if affil != '':
|
if affil != '':
|
||||||
outfile.write(", "+affil+"\n")
|
outfile.write(", "+affil+"\n")
|
||||||
|
|
@ -263,8 +263,8 @@ with open(output_file,"w") as outfile:
|
||||||
checkin_count = 0
|
checkin_count = 0
|
||||||
checkin_str = ""
|
checkin_str = ""
|
||||||
outfile.write(" "+call+", "+call_data_dict[call][0]+checkin_str)
|
outfile.write(" "+call+", "+call_data_dict[call][0]+checkin_str)
|
||||||
if call in hoip_phone_dict.keys():
|
if call in hh_phone_dict.keys():
|
||||||
outfile.write(", HOIP #"+hoip_phone_dict[call])
|
outfile.write(", HH VOIP #"+hh_phone_dict[call])
|
||||||
affil = call_data_dict[call][4]
|
affil = call_data_dict[call][4]
|
||||||
if affil != '':
|
if affil != '':
|
||||||
outfile.write(", "+affil+"\n")
|
outfile.write(", "+affil+"\n")
|
||||||
|
|
@ -337,21 +337,14 @@ for net_day in net_day_list:
|
||||||
with open(output_file,"w") as outfile:
|
with open(output_file,"w") as outfile:
|
||||||
|
|
||||||
# Opening text
|
# Opening text
|
||||||
headertext = '\n'.join([
|
outfile.write("\n ")
|
||||||
'PNW Digital ARES & EmComm Check-In Net - '+str(num_checkins)+
|
outfile.write("PNW Digital ARES & EMCOMM Check-In Net - "+str(num_checkins)+" check-ins on "+net_day+"\n")
|
||||||
' check-ins on '+net_day+'\n',
|
outfile.write("\n")
|
||||||
'There were '+str(num_checkins)+' check-ins on '+net_day+' to the Pacific',
|
outfile.write("We had a total of "+str(num_checkins)+" check-ins on "+net_day+" to the Pacific Northwest (PNW)\n")
|
||||||
'Northwest (PNW) Digital ARES & EmComm Check-In Net.',
|
outfile.write("Digital ARES & EMCOMM Check-In Net. Below is the detailed check-in list grouped\n")
|
||||||
' ',
|
outfile.write("by ARRL ARES districts. If any of the info (i.e. name or agency affiliation) below is\n")
|
||||||
'The following is the detailed check-in list grouped',
|
outfile.write("incomplete or incorrect, please IM me on Jabber/XMPP (ed@n7ekb.net), or\n")
|
||||||
'by our region\'s ARRL ARES districts. If any of the',
|
outfile.write("e-mail \"ed@n7ekb.net\" with the correction(s).\n\n\n")
|
||||||
'info (i.e. name or agency affiliation) below is',
|
|
||||||
'incomplete or incorrect, please IM me on Jabber/',
|
|
||||||
'XMPP at n7ekb@n7ekb.net, e-mail me at ed@n7ekb.net, or',
|
|
||||||
'call me on HOIP extension 103675 with your updates.',
|
|
||||||
'\n\n'
|
|
||||||
])
|
|
||||||
outfile.write(headertext)
|
|
||||||
|
|
||||||
# Check-in details
|
# Check-in details
|
||||||
for state in sorted(report_dict):
|
for state in sorted(report_dict):
|
||||||
|
|
@ -367,10 +360,8 @@ for net_day in net_day_list:
|
||||||
cur_call_list.sort()
|
cur_call_list.sort()
|
||||||
for call in cur_call_list:
|
for call in cur_call_list:
|
||||||
affil = call_data_dict[call][4]
|
affil = call_data_dict[call][4]
|
||||||
hoip_num = call_data_dict[call][5]
|
hh_num = call_data_dict[call][5]
|
||||||
outfile.write(" "+call+", "+call_data_dict[call][0])
|
outfile.write(" "+call+", "+call_data_dict[call][0])
|
||||||
if hoip_num != '':
|
|
||||||
outfile.write(", HOIP #"+hoip_num)
|
|
||||||
if affil != '':
|
if affil != '':
|
||||||
outfile.write(", "+affil+"\n")
|
outfile.write(", "+affil+"\n")
|
||||||
else:
|
else:
|
||||||
|
|
@ -382,10 +373,8 @@ for net_day in net_day_list:
|
||||||
cur_call_list.sort()
|
cur_call_list.sort()
|
||||||
for call in cur_call_list:
|
for call in cur_call_list:
|
||||||
affil = call_data_dict[call][4]
|
affil = call_data_dict[call][4]
|
||||||
hoip_num = call_data_dict[call][5]
|
hh_num = call_data_dict[call][5]
|
||||||
outfile.write(" "+call+", "+call_data_dict[call][0])
|
outfile.write(" "+call+", "+call_data_dict[call][0])
|
||||||
if hoip_num != '':
|
|
||||||
outfile.write(", HOIP #"+hoip_num)
|
|
||||||
if affil != '':
|
if affil != '':
|
||||||
outfile.write(", "+affil+"\n")
|
outfile.write(", "+affil+"\n")
|
||||||
else:
|
else:
|
||||||
|
|
@ -401,10 +390,8 @@ for net_day in net_day_list:
|
||||||
cur_call_list.sort()
|
cur_call_list.sort()
|
||||||
for call in cur_call_list:
|
for call in cur_call_list:
|
||||||
affil = call_data_dict[call][4]
|
affil = call_data_dict[call][4]
|
||||||
hoip_num = call_data_dict[call][5]
|
hh_num = call_data_dict[call][5]
|
||||||
outfile.write(" "+call+", "+call_data_dict[call][0])
|
outfile.write(" "+call+", "+call_data_dict[call][0])
|
||||||
if hoip_num != '':
|
|
||||||
outfile.write(", HOIP #"+hoip_num)
|
|
||||||
if affil != '':
|
if affil != '':
|
||||||
outfile.write(", "+affil+"\n")
|
outfile.write(", "+affil+"\n")
|
||||||
else:
|
else:
|
||||||
|
|
@ -413,24 +400,14 @@ for net_day in net_day_list:
|
||||||
outfile.write("\n")
|
outfile.write("\n")
|
||||||
|
|
||||||
# output the footer text...
|
# output the footer text...
|
||||||
footer = '\n'.join([
|
outfile.write("\nAbout the net:\n\n")
|
||||||
'About the net:\n',
|
outfile.write("The PNW Digital ARES & EMCOMM Check-In Net is held every Sunday evening at 7:30 PM local\n")
|
||||||
'The PNW Digital ARES & EMCOMM Check-In Net is held',
|
outfile.write("time on PNW Regional, DMR talk group 31771 (available on both the PNW Digital http://pnwdigital.net\n")
|
||||||
'every Sunday evening at 7:30 PM local time on PNW',
|
outfile.write("and Brandmeister DMR networks). Anyone interested in Amateur Radio Emergency Communications is\n")
|
||||||
'Regional, DMR talkgroup 31771. DMR Talk Group 31771 is',
|
outfile.write("welcome to check-in. The net is an opportunity for DMR-capable ARES and EMCOMM hams to exercise\n")
|
||||||
'available on both the PNW Digital and the Brandmeister',
|
outfile.write("their DMR equipment in a regional directed net. The net demonstrates the wide coverage area and\n")
|
||||||
'DMR networks. Anyone interested in Amateur Radio and',
|
outfile.write("capability of DMR repeaters and hot spots in our Pacific Northwest region. It also highlights the\n")
|
||||||
'Emergency Communications is welcome to check-in. The',
|
outfile.write("wide range of EMCOMM-related organizations who have members with DMR capability.\n\n")
|
||||||
'net is an opportunity for DMR-capable ARES and EmComm',
|
|
||||||
'hams to exercise their DMR equipment in a regional,',
|
|
||||||
'directed net. The net demonstrates the wide coverage',
|
|
||||||
'area and capability of DMR repeaters and hot spots in',
|
|
||||||
'our Pacific Northwest region. It also highlights the',
|
|
||||||
'wide range of EmComm-related organizations who have',
|
|
||||||
'members with DMR capability.\n\n',
|
|
||||||
'#net-reports #hamradio #EmComm #ARES #DMR #HOIP #PNWDigital\n'
|
|
||||||
])
|
|
||||||
outfile.write(footer)
|
|
||||||
|
|
||||||
# close this week's report file
|
# close this week's report file
|
||||||
outfile.close()
|
outfile.close()
|
||||||
|
|
|
||||||
6203
hoip_subscribers.csv
6203
hoip_subscribers.csv
File diff suppressed because it is too large
Load diff
|
|
@ -1,56 +0,0 @@
|
||||||
from ldap3 import Server, Connection, ALL, SUBTREE
|
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
# LDAP connection details from the wiki
|
|
||||||
server_url = 'ldap://207.246.98.219:389' # Server and port (no TLS, as per "TLS Mode: LDAP or No" – use 'ldaps://' if TLS needed)
|
|
||||||
search_base = 'ou=people,dc=hamsoverip,dc=com'
|
|
||||||
search_filter = '(telephoneNumber=*)' # Fetch all entries with an extension; adjust if needed, e.g., '(objectClass=person)'
|
|
||||||
attributes = ['cn', 'sn', 'telephoneNumber'] # Key attributes; add more if you discover others like 'callsign'
|
|
||||||
|
|
||||||
# Connect anonymously (no auth)
|
|
||||||
server = Server(server_url, get_info=ALL)
|
|
||||||
conn = Connection(server, auto_bind=True) # No user/password
|
|
||||||
|
|
||||||
# Perform the search
|
|
||||||
conn.search(
|
|
||||||
search_base=search_base,
|
|
||||||
search_filter=search_filter,
|
|
||||||
search_scope=SUBTREE,
|
|
||||||
attributes=attributes
|
|
||||||
)
|
|
||||||
|
|
||||||
# Extract data
|
|
||||||
data = []
|
|
||||||
for entry in conn.entries:
|
|
||||||
extension = entry.telephoneNumber.value if 'telephoneNumber' in entry else ''
|
|
||||||
cn_value = entry.cn.value if 'cn' in entry else ''
|
|
||||||
sn_value = entry.sn.value if 'sn' in entry else ''
|
|
||||||
|
|
||||||
# clean up data
|
|
||||||
hoipnumber = extension.partition(' ')[0]
|
|
||||||
callsign = cn_value.partition(' ')[0]
|
|
||||||
hamname = sn_value.partition(' ')[0]
|
|
||||||
|
|
||||||
if hoipnumber: # Only include entries with extensions
|
|
||||||
data.append({
|
|
||||||
'HOIP Extension': hoipnumber,
|
|
||||||
'Call': callsign,
|
|
||||||
'Name': hamname # Include raw for reference
|
|
||||||
})
|
|
||||||
|
|
||||||
# Save to CSV
|
|
||||||
if data:
|
|
||||||
df = pd.DataFrame(data)
|
|
||||||
|
|
||||||
# CSV output for debugging...
|
|
||||||
df.to_csv('hoip_subscribers.csv', index=False)
|
|
||||||
print(f'Saved {len(data)} entries to hoip_subscribers.csv')
|
|
||||||
|
|
||||||
# Excel output for production...
|
|
||||||
df.to_excel('hoip_subscribers.xlsx', index=False)
|
|
||||||
print(f'Saved {len(data)} entries to hoip_subscribers.xlsx')
|
|
||||||
else:
|
|
||||||
print('No entries found – check filter or connection.')
|
|
||||||
|
|
||||||
# Unbind
|
|
||||||
conn.unbind()
|
|
||||||
14
shell.nix
14
shell.nix
|
|
@ -1,14 +0,0 @@
|
||||||
# shell.nix
|
|
||||||
{ pkgs ? import <nixpkgs> {} }:
|
|
||||||
|
|
||||||
pkgs.mkShell {
|
|
||||||
buildInputs = [
|
|
||||||
(pkgs.python3.withPackages (ps: with ps; [
|
|
||||||
numpy
|
|
||||||
openpyxl
|
|
||||||
pandas
|
|
||||||
python-dateutil
|
|
||||||
]))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue