Revert "Adding Flake8 Github Action for Python Linting "

This commit is contained in:
Patrick McGuire
2022-05-11 09:05:59 -04:00
committed by GitHub
parent bffd6a889d
commit 528802a0e6
6 changed files with 256 additions and 473 deletions
-3
View File
@@ -1,3 +0,0 @@
[flake8]
max-line-length = 128
-23
View File
@@ -1,23 +0,0 @@
name: CI Jobs
on: pull_request
jobs:
python-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v3
with:
python-version: '3.9.x'
cache: 'pip'
architecture: 'x64'
- name: Install flake8
run: pip install flake8
- name: Run Flake8 Lint
uses: py-actions/flake8@v2
+16 -72
View File
@@ -1,16 +1,3 @@
#!/usr/bin/env python3
"""
# Example calls
python3 analyze.py --i 'example/XC558716 - Soundscape.mp3' \
--lat 35.4244 --lon -120.7463 --week 18
python3 analyze.py --i 'example/XC563936 - Soundscape.mp3' \
--lat 47.6766 --lon -122.294 --week 11 \
--overlap 1.5 --min_conf 0.25 --sensitivity 1.25 \
--custom_list 'example/custom_species_list.txt'
"""
import argparse import argparse
import socket import socket
@@ -24,7 +11,6 @@ ADDR = (SERVER, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR) client.connect(ADDR)
def send(msg): def send(msg):
message = msg.encode(FORMAT) message = msg.encode(FORMAT)
msg_length = len(message) msg_length = len(message)
@@ -34,7 +20,6 @@ def send(msg):
client.send(message) client.send(message)
print(client.recv(2048).decode(FORMAT)) print(client.recv(2048).decode(FORMAT))
def main(): def main():
global INCLUDE_LIST global INCLUDE_LIST
@@ -42,62 +27,17 @@ def main():
# Parse passed arguments # Parse passed arguments
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument('--i', help='Path to input file.')
'--i', parser.add_argument('--o', default='result.csv', help='Path to output file. Defaults to result.csv.')
help='Path to input file.') parser.add_argument('--lat', type=float, default=-1, help='Recording location latitude. Set -1 to ignore.')
parser.add_argument( parser.add_argument('--lon', type=float, default=-1, help='Recording location longitude. Set -1 to ignore.')
'--o', parser.add_argument('--week', type=int, default=-1, help='Week of the year when the recording was made. Values in [1, 48] (4 weeks per month). Set -1 to ignore.')
default='result.csv', parser.add_argument('--overlap', type=float, default=0.0, help='Overlap in seconds between extracted spectrograms. Values in [0.0, 2.9]. Defaults tp 0.0.')
help='Path to output file. Defaults to result.csv.') parser.add_argument('--sensitivity', type=float, default=1.0, help='Detection sensitivity; Higher values result in higher sensitivity. Values in [0.5, 1.5]. Defaults to 1.0.')
parser.add_argument( parser.add_argument('--min_conf', type=float, default=0.1, help='Minimum confidence threshold. Values in [0.01, 0.99]. Defaults to 0.1.')
'--lat', parser.add_argument('--include_list', default='null', help='Path to text file containing a list of included species. Not used if not provided.')
type=float, parser.add_argument('--exclude_list', default='null', help='Path to text file containing a list of excluded species. Not used if not provided.')
default=-1, parser.add_argument('--birdweather_id', default='99999', help='Private Station ID for BirdWeather.')
help='Recording location latitude. Set -1 to ignore.')
parser.add_argument(
'--lon',
type=float,
default=-1,
help='Recording location longitude. Set -1 to ignore.')
parser.add_argument(
'--week',
type=int,
default=-1,
help='''Week of the year when the recording was made.
Values in [1, 48] (4 weeks per month). Set -1 to ignore.''')
parser.add_argument(
'--overlap',
type=float,
default=0.0,
help='''Overlap in seconds between extracted spectrograms.
Values in [0.0, 2.9]. Defaults tp 0.0.''')
parser.add_argument(
'--sensitivity',
type=float,
default=1.0,
help='''Detection sensitivity;
Higher values result in higher sensitivity.
Values in [0.5, 1.5]. Defaults to 1.0.''')
parser.add_argument(
'--min_conf',
type=float,
default=0.1,
help='''Minimum confidence threshold.
Values in [0.01, 0.99]. Defaults to 0.1.''')
parser.add_argument(
'--include_list',
default='null',
help='''Path to text file containing a list of included species.
Not used if not provided.''')
parser.add_argument(
'--exclude_list',
default='null',
help='''Path to text file containing a list of excluded species.
Not used if not provided.''')
parser.add_argument(
'--birdweather_id',
default='99999',
help='Private Station ID for BirdWeather.')
args = parser.parse_args() args = parser.parse_args()
@@ -133,6 +73,10 @@ def main():
############################################################################### ###############################################################################
############################################################################### ###############################################################################
if __name__ == '__main__': if __name__ == '__main__':
main() main()
# Example calls
# python3 analyze.py --i 'example/XC558716 - Soundscape.mp3' --lat 35.4244 --lon -120.7463 --week 18
# python3 analyze.py --i 'example/XC563936 - Soundscape.mp3' --lat 47.6766 --lon -122.294 --week 11 --overlap 1.5 --min_conf 0.25 --sensitivity 1.25 --custom_list 'example/custom_species_list.txt'
+26 -92
View File
@@ -1,5 +1,6 @@
import sqlite3 import sqlite3
import os import os
import configparser
import pandas as pd import pandas as pd
import seaborn as sns import seaborn as sns
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
@@ -11,8 +12,7 @@ userDir = os.path.expanduser('~')
conn = sqlite3.connect(userDir + '/BirdNET-Pi/scripts/birds.db') conn = sqlite3.connect(userDir + '/BirdNET-Pi/scripts/birds.db')
df = pd.read_sql_query("SELECT * from detections", conn) df = pd.read_sql_query("SELECT * from detections", conn)
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute( cursor.execute('SELECT * FROM detections WHERE Date = DATE(\'now\', \'localtime\')')
'SELECT * FROM detections WHERE Date = DATE(\'now\', \'localtime\')')
table_rows = cursor.fetchall() table_rows = cursor.fetchall()
@@ -37,29 +37,17 @@ df_plt_today = df_plt[df_plt['Date'] == now.strftime("%Y-%m-%d")]
readings=10 readings=10
plt_top10_today = (df_plt_today['Com_Name'].value_counts()[:readings]) plt_top10_today = (df_plt_today['Com_Name'].value_counts()[:readings])
df_plt_top10_today = df_plt_today[df_plt_today.Com_Name.isin( df_plt_top10_today = df_plt_today[df_plt_today.Com_Name.isin(plt_top10_today.index)]
plt_top10_today.index)]
#Set Palette for graphics #Set Palette for graphics
pal = "Greens" pal = "Greens"
#Set up plot axes and titles #Set up plot axes and titles
f, axs = plt.subplots( f, axs = plt.subplots(1, 2, figsize = (10, 4), gridspec_kw=dict(width_ratios=[3, 6]), facecolor='#77C487')
1, 2, figsize=( plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0, hspace=0)
10, 4), gridspec_kw=dict(
width_ratios=[
3, 6]), facecolor='#77C487')
plt.subplots_adjust(
left=None,
bottom=None,
right=None,
top=None,
wspace=0,
hspace=0)
#generate y-axis order for all figures based on frequency #generate y-axis order for all figures based on frequency
freq_order = pd.value_counts( freq_order = pd.value_counts(df_plt_top10_today['Com_Name']).iloc[:readings].index
df_plt_top10_today['Com_Name']).iloc[:readings].index
#make color for max confidence --> this groups by name and calculates max conf #make color for max confidence --> this groups by name and calculates max conf
confmax = df_plt_top10_today.groupby('Com_Name')['Confidence'].max() confmax = df_plt_top10_today.groupby('Com_Name')['Confidence'].max()
@@ -71,28 +59,21 @@ norm = plt.Normalize(confmax.values.min(), confmax.values.max())
colors = plt.cm.Greens(norm(confmax)) colors = plt.cm.Greens(norm(confmax))
#Generate frequency plot #Generate frequency plot
plot = sns.countplot( plot=sns.countplot(y='Com_Name', data = df_plt_top10_today, palette = colors, order=freq_order, ax=axs[0])
y='Com_Name',
data=df_plt_top10_today,
palette=colors,
order=freq_order,
ax=axs[0])
# Try plot grid lines between bars - problem at the moment plots grid
# lines on bars - want between bars
#Try plot grid lines between bars - problem at the moment plots grid lines on bars - want between bars
z=plot.get_ymajorticklabels() z=plot.get_ymajorticklabels()
plot.set_yticklabels(['\n'.join(textwrap.wrap(ticklabel.get_text(), 15)) plot.set_yticklabels(['\n'.join(textwrap.wrap(ticklabel.get_text(),15)) for ticklabel in plot.get_yticklabels()], fontsize = 10)
for ticklabel in plot.get_yticklabels()], fontsize=10)
plot.set(ylabel=None) plot.set(ylabel=None)
plot.set(xlabel="Detections") plot.set(xlabel="Detections")
#Generate crosstab matrix for heatmap plot #Generate crosstab matrix for heatmap plot
heat = pd.crosstab( heat = pd.crosstab(df_plt_top10_today['Com_Name'],df_plt_top10_today['Hour of Day'])
df_plt_top10_today['Com_Name'],
df_plt_top10_today['Hour of Day'])
#Order heatmap Birds by frequency of occurrance #Order heatmap Birds by frequency of occurrance
heat.index = pd.CategoricalIndex(heat.index, categories = freq_order) heat.index = pd.CategoricalIndex(heat.index, categories = freq_order)
heat.sort_index(level=0, inplace=True) heat.sort_index(level=0, inplace=True)
@@ -103,20 +84,7 @@ heat_frame = pd.DataFrame(data=0, index=heat.index, columns=hours_in_day)
heat=(heat+heat_frame).fillna(0) heat=(heat+heat_frame).fillna(0)
#Generatie heatmap plot #Generatie heatmap plot
plot = sns.heatmap( plot = sns.heatmap(heat, norm=LogNorm(), annot=True, annot_kws={"fontsize":7}, fmt="g", cmap = pal , square = False, cbar=False, linewidths = 0.5, linecolor = "Grey", ax=axs[1], yticklabels = False)
heat,
norm=LogNorm(),
annot=True,
annot_kws={
"fontsize": 7},
fmt="g",
cmap=pal,
square=False,
cbar=False,
linewidths=0.5,
linecolor="Grey",
ax=axs[1],
yticklabels=False)
plot.set_xticklabels(plot.get_xticklabels(), rotation = 0, size = 7) plot.set_xticklabels(plot.get_xticklabels(), rotation = 0, size = 7)
# Set heatmap border # Set heatmap border
@@ -131,8 +99,7 @@ plt.suptitle("Top 10 Last Updated: " + str(now.strftime("%Y-%m-%d %H:%M")))
#Save combined plot #Save combined plot
userDir = os.path.expanduser('~') userDir = os.path.expanduser('~')
savename = userDir + '/BirdSongs/Extracted/Charts/Combo-' + \ savename=userDir + '/BirdSongs/Extracted/Charts/Combo-'+str(now.strftime("%Y-%m-%d"))+'.png'
str(now.strftime("%Y-%m-%d")) + '.png'
plt.savefig(savename) plt.savefig(savename)
plt.show() plt.show()
plt.close() plt.close()
@@ -140,30 +107,18 @@ plt.close()
# Get Bottom detection frequency # Get Bottom detection frequency
plt_Bot10_today = (df_plt_today['Com_Name'].value_counts()[-readings:]) plt_Bot10_today = (df_plt_today['Com_Name'].value_counts()[-readings:])
df_plt_Bot10_today = df_plt_today[df_plt_today.Com_Name.isin( df_plt_Bot10_today = df_plt_today[df_plt_today.Com_Name.isin(plt_Bot10_today.index)]
plt_Bot10_today.index)]
#Set Palette for graphics #Set Palette for graphics
pal = "Reds" pal = "Reds"
#Set up plot axes and titles #Set up plot axes and titles
f, axs = plt.subplots( f, axs = plt.subplots(1, 2, figsize = (10, 4), gridspec_kw=dict(width_ratios=[3, 6]), facecolor='#77C487')
1, 2, figsize=( plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0, hspace=0)
10, 4), gridspec_kw=dict(
width_ratios=[
3, 6]), facecolor='#77C487')
plt.subplots_adjust(
left=None,
bottom=None,
right=None,
top=None,
wspace=0,
hspace=0)
#generate y-axis order for all figures based on frequency #generate y-axis order for all figures based on frequency
freq_order = pd.value_counts( freq_order = pd.value_counts(df_plt_Bot10_today['Com_Name']).iloc[-readings:].index
df_plt_Bot10_today['Com_Name']).iloc[-readings:].index
#make color for max confidence --> this groups by name and calculates max conf #make color for max confidence --> this groups by name and calculates max conf
confmax = df_plt_Bot10_today.groupby('Com_Name')['Confidence'].max() confmax = df_plt_Bot10_today.groupby('Com_Name')['Confidence'].max()
@@ -174,27 +129,20 @@ norm = plt.Normalize(confmax.values.min(), confmax.values.max())
colors = plt.cm.Reds(norm(confmax)) colors = plt.cm.Reds(norm(confmax))
#Generate frequency plot #Generate frequency plot
plot = sns.countplot( plot=sns.countplot(y='Com_Name', data = df_plt_Bot10_today, palette = colors, order=freq_order, ax=axs[0])
y='Com_Name',
data=df_plt_Bot10_today,
palette=colors,
order=freq_order,
ax=axs[0])
# Try plot grid lines between bars - problem at the moment plots grid
# lines on bars - want between bars
#Try plot grid lines between bars - problem at the moment plots grid lines on bars - want between bars
z=plot.get_ymajorticklabels() z=plot.get_ymajorticklabels()
plot.set_yticklabels(['\n'.join(textwrap.wrap(ticklabel.get_text(), 15)) plot.set_yticklabels(['\n'.join(textwrap.wrap(ticklabel.get_text(),15)) for ticklabel in plot.get_yticklabels()], fontsize = 10)
for ticklabel in plot.get_yticklabels()], fontsize=10)
plot.set(ylabel=None) plot.set(ylabel=None)
plot.set(xlabel="Detections") plot.set(xlabel="Detections")
#Generate crosstab matrix for heatmap plot #Generate crosstab matrix for heatmap plot
heat = pd.crosstab( heat = pd.crosstab(df_plt_Bot10_today['Com_Name'],df_plt_Bot10_today['Hour of Day'])
df_plt_Bot10_today['Com_Name'],
df_plt_Bot10_today['Hour of Day'])
#Order heatmap Birds by frequency of occurrance #Order heatmap Birds by frequency of occurrance
heat.index = pd.CategoricalIndex(heat.index, categories = freq_order) heat.index = pd.CategoricalIndex(heat.index, categories = freq_order)
heat.sort_index(level=0, inplace=True) heat.sort_index(level=0, inplace=True)
@@ -205,20 +153,7 @@ heat_frame = pd.DataFrame(data=0, index=heat.index, columns=hours_in_day)
heat=(heat+heat_frame).fillna(0) heat=(heat+heat_frame).fillna(0)
#Generatie heatmap plot #Generatie heatmap plot
plot = sns.heatmap( plot = sns.heatmap(heat, norm=LogNorm(), annot=True, fmt="g", annot_kws={"fontsize":7}, cmap = pal , square = False, cbar=False, linewidths = 0.5, linecolor = "Grey", ax=axs[1], yticklabels = False)
heat,
norm=LogNorm(),
annot=True,
fmt="g",
annot_kws={
"fontsize": 7},
cmap=pal,
square=False,
cbar=False,
linewidths=0.5,
linecolor="Grey",
ax=axs[1],
yticklabels=False)
plot.set_xticklabels(plot.get_xticklabels(), rotation = 0, size = 7) plot.set_xticklabels(plot.get_xticklabels(), rotation = 0, size = 7)
# Set heatmap border # Set heatmap border
@@ -232,8 +167,7 @@ f.subplots_adjust(top=0.9)
plt.suptitle("Bottom 10 Last Updated: "+ str(now.strftime("%Y-%m-%d %H:%M"))) plt.suptitle("Bottom 10 Last Updated: "+ str(now.strftime("%Y-%m-%d %H:%M")))
#Save combined plot #Save combined plot
savename = userDir + '/BirdSongs/Extracted/Charts/Combo2-' + \ savename=userDir + '/BirdSongs/Extracted/Charts/Combo2-'+str(now.strftime("%Y-%m-%d"))+'.png'
str(now.strftime("%Y-%m-%d")) + '.png'
plt.savefig(savename) plt.savefig(savename)
plt.show() plt.show()
plt.close() plt.close()
+12 -20
View File
@@ -4,7 +4,8 @@ import pandas as pd
import numpy as np import numpy as np
import plotly.graph_objects as go import plotly.graph_objects as go
from plotly.subplots import make_subplots from plotly.subplots import make_subplots
from datetime import timedelta from datetime import timedelta, datetime
from pathlib import Path
import sqlite3 import sqlite3
from sqlite3 import Connection from sqlite3 import Connection
@@ -41,7 +42,6 @@ def get_data(conn: Connection):
df1=pd.read_sql("SELECT * FROM detections", con=conn) df1=pd.read_sql("SELECT * FROM detections", con=conn)
return df1 return df1
conn = get_connection(URI_SQLITE_DB) conn = get_connection(URI_SQLITE_DB)
# Read in the cereal data # Read in the cereal data
# df = load_data() # df = load_data()
@@ -51,6 +51,7 @@ df2['DateTime'] = pd.to_datetime(df2['Date'] + " " + df2['Time'])
df2=df2.set_index('DateTime') df2=df2.set_index('DateTime')
# Filter on date range # Filter on date range
# Date as calendars # Date as calendars
# Start_Date = pd.to_datetime(st.sidebar.date_input('Which date do you want to start?', value = df2.index.min())) # Start_Date = pd.to_datetime(st.sidebar.date_input('Which date do you want to start?', value = df2.index.min()))
@@ -67,6 +68,7 @@ Date_Slider = st.slider('Date Range',
) )
filt = (df2.index >= pd.Timestamp(Date_Slider[0])) & (df2.index <= pd.Timestamp(Date_Slider[1]+timedelta(days=1))) filt = (df2.index >= pd.Timestamp(Date_Slider[0])) & (df2.index <= pd.Timestamp(Date_Slider[1]+timedelta(days=1)))
df2 = df2[filt] df2 = df2[filt]
@@ -92,10 +94,7 @@ top_N = cols1.slider(
top_N_species = (df2['Com_Name'].value_counts()[:top_N]) top_N_species = (df2['Com_Name'].value_counts()[:top_N])
specie = cols2.selectbox( specie = cols2.selectbox('Which bird would you like to explore for the dates '+str(Date_Slider[0])+' to '+str(Date_Slider[1])+'?', species,
'Which bird would you like to explore for the dates ' +
str(Date_Slider[0]) + ' to ' + str(Date_Slider[1]) + '?',
species,
index=species.index(list(top_N_species.index)[0])) index=species.index(list(top_N_species.index)[0]))
@@ -109,17 +108,11 @@ df_counts = df2[filt].resample('D').count()
fig = make_subplots( fig = make_subplots(
rows=3, cols =2, rows=3, cols =2,
specs=[[{"type": "xy", "rowspan": 3}, {"type": "polar", "rowspan": 2}], [ specs= [[{"type":"xy","rowspan":3}, {"type":"polar","rowspan":2}], [{"rowspan":1}, {"rowspan":1} ], [None, {"type":"xy","rowspan":1}]],
{"rowspan": 1}, {"rowspan": 1}], [None, {"type": "xy", "rowspan": 1}]], subplot_titles=('<b>Top '+ str(top_N) + ' Species in Date Range '+str(Date_Slider[0])+' to '+str(Date_Slider[1])+'</b>',
subplot_titles=(
'<b>Top ' + str(top_N) +
' Species in Date Range ' + str(Date_Slider[0]) +
' to ' + str(Date_Slider[1]) +
'</b>',
'Total Detect:'+str('{:,}'.format(sum(df_counts.Time)))+ 'Total Detect:'+str('{:,}'.format(sum(df_counts.Time)))+
' Confidence Max:'+str('{:.2f}%'.format(max(df2[df2['Com_Name']==specie]['Confidence'])*100))+ ' Confidence Max:'+str('{:.2f}%'.format(max(df2[df2['Com_Name']==specie]['Confidence'])*100))+
' ' + ' Median:' + ' '+' Median:'+str('{:.2f}%'.format(np.median(df2[df2['Com_Name']==specie]['Confidence'])*100))
str('{:.2f}%'.format(np.median(df2[df2['Com_Name'] == specie]['Confidence']) * 100))
) )
) )
fig.layout.annotations[1].update(x=0.7,y=0.25, font_size=15) fig.layout.annotations[1].update(x=0.7,y=0.25, font_size=15)
@@ -154,16 +147,15 @@ fig.update_layout(
rotation = -90, rotation = -90,
direction = 'clockwise', direction = 'clockwise',
tickmode='array', tickmode='array',
tickvals=[0, 15, 35, 45, 60, 75, 90, 105, 120, 135, 150, 165, tickvals=[0,15,35,45,60,75,90,105,120,135,150,165,180,195,210,225,240,255,270,285,300,315,330,345],
180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345], ticktext=['12am','1am','2am','3am','4am','5am', '6am','7am','8am','9am','10am','11am','12pm','1pm','2pm','3pm','4pm','5pm', '6pm','7pm','8pm','9pm','10pm','11pm'],
ticktext=['12am', '1am', '2am', '3am', '4am', '5am', '6am', '7am', '8am', '9am', '10am', '11am',
'12pm', '1pm', '2pm', '3pm', '4pm', '5pm', '6pm', '7pm', '8pm', '9pm', '10pm', '11pm'],
hoverformat = "#%{theta}: <br>Popularity: %{percent} </br> %{r}" hoverformat = "#%{theta}: <br>Popularity: %{percent} </br> %{r}"
), ),
), ),
) )
daily=pd.crosstab(df2['Com_Name'],df2.index.date, dropna=False) daily=pd.crosstab(df2['Com_Name'],df2.index.date, dropna=False)
fig.add_trace(go.Bar(x=daily.columns, y=daily.loc[specie]), row=3, col=2) fig.add_trace(go.Bar(x=daily.columns, y=daily.loc[specie]), row=3, col=2)
@@ -176,6 +168,6 @@ st.plotly_chart(fig, use_container_width=True) # , config=config)
# #
# extract_date=Date_Slider # extract_date=Date_Slider
# #
# audio_file = open('/home/*/BirdSongs/Extracted/By_Date/2022-03-22/Yellow-streaked_Greenbul/Yellow-streaked_Greenbul-77-2022-03-22-birdnet-15:04:28.mp3', 'rb') # noqa: E501 # audio_file = open('/home/*/BirdSongs/Extracted/By_Date/2022-03-22/Yellow-streaked_Greenbul/Yellow-streaked_Greenbul-77-2022-03-22-birdnet-15:04:28.mp3', 'rb')
# audio_bytes = audio_file.read() # audio_bytes = audio_file.read()
# cols4.audio(audio_bytes, format='audio/mp3') # cols4.audio(audio_bytes, format='audio/mp3')
+45 -106
View File
@@ -1,27 +1,32 @@
import os
import socket import socket
import threading import threading
import operator import os
import librosa
import numpy as np
import math
import time
import json
import requests
import sqlite3
import datetime
from tzlocal import get_localzone
from pathlib import Path
import apprise
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
os.environ['CUDA_VISIBLE_DEVICES'] = '' os.environ['CUDA_VISIBLE_DEVICES'] = ''
try: try:
import tflite_runtime.interpreter as tflite import tflite_runtime.interpreter as tflite
except BaseException: except:
from tensorflow import lite as tflite from tensorflow import lite as tflite
import argparse
import operator
import librosa
import numpy as np
import math
import time
from decimal import Decimal
import json
import requests
import sqlite3
import datetime
from time import sleep
import pytz
from tzlocal import get_localzone
from pathlib import Path
import apprise
HEADER = 64 HEADER = 64
PORT = 5050 PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname()) SERVER = socket.gethostbyname(socket.gethostname())
@@ -32,18 +37,18 @@ DISCONNECT_MESSAGE = "!DISCONNECT"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: try:
server.bind(ADDR) server.bind(ADDR)
except BaseException: except:
print("Waiting on socket") print("Waiting on socket")
time.sleep(5) time.sleep(5)
# Open most recent Configuration and grab DB_PWD as a python variable # Open most recent Configuration and grab DB_PWD as a python variable
userDir = os.path.expanduser('~') userDir = os.path.expanduser('~')
with open(userDir + '/BirdNET-Pi/scripts/thisrun.txt', 'r') as f: with open(userDir + '/BirdNET-Pi/scripts/thisrun.txt', 'r') as f:
this_run = f.readlines() this_run = f.readlines()
audiofmt = "." + str(str(str([i for i in this_run if i.startswith('AUDIOFMT')]).split('=')[1]).split('\\')[0]) audiofmt = "." + str(str(str([i for i in this_run if i.startswith('AUDIOFMT')]).split('=')[1]).split('\\')[0])
priv_thresh = float( priv_thresh = float("." + str(str(str([i for i in this_run if i.startswith('PRIVACY_THRESHOLD')]).split('=')[1]).split('\\')[0]))/10
"." + str(str(str([i for i in this_run if i.startswith('PRIVACY_THRESHOLD')]).split('=')[1]).split('\\')[0])) / 10
def loadModel(): def loadModel():
@@ -80,7 +85,6 @@ def loadModel():
return myinterpreter return myinterpreter
def loadCustomSpeciesList(path): def loadCustomSpeciesList(path):
slist = [] slist = []
@@ -91,7 +95,6 @@ def loadCustomSpeciesList(path):
return slist return slist
def splitSignal(sig, rate, overlap, seconds=3.0, minlen=1.5): def splitSignal(sig, rate, overlap, seconds=3.0, minlen=1.5):
# Split signal with overlap # Split signal with overlap
@@ -113,7 +116,6 @@ def splitSignal(sig, rate, overlap, seconds=3.0, minlen=1.5):
return sig_splits return sig_splits
def readAudioData(path, overlap, sample_rate=48000): def readAudioData(path, overlap, sample_rate=48000):
print('READING AUDIO DATA...', end=' ', flush=True) print('READING AUDIO DATA...', end=' ', flush=True)
@@ -128,7 +130,6 @@ def readAudioData(path, overlap, sample_rate=48000):
return chunks return chunks
def convertMetadata(m): def convertMetadata(m):
# Convert week to cosine # Convert week to cosine
@@ -146,11 +147,9 @@ def convertMetadata(m):
return np.concatenate([m, mask]) return np.concatenate([m, mask])
def custom_sigmoid(x, sensitivity=1.0): def custom_sigmoid(x, sensitivity=1.0):
return 1 / (1.0 + np.exp(-sensitivity * x)) return 1 / (1.0 + np.exp(-sensitivity * x))
def predict(sample, sensitivity): def predict(sample, sensitivity):
global INTERPRETER global INTERPRETER
# Make a prediction # Make a prediction
@@ -182,7 +181,6 @@ def predict(sample, sensitivity):
return p_sorted[:human_cutoff] return p_sorted[:human_cutoff]
def analyzeAudioData(chunks, lat, lon, week, sensitivity, overlap,): def analyzeAudioData(chunks, lat, lon, week, sensitivity, overlap,):
global INTERPRETER global INTERPRETER
@@ -215,7 +213,7 @@ def analyzeAudioData(chunks, lat, lon, week, sensitivity, overlap,):
pred_end = pred_start + 3.0 pred_end = pred_start + 3.0
#If human detected set all detections to human to make sure voices are not saved #If human detected set all detections to human to make sure voices are not saved
if HUMAN_DETECTED is True: if HUMAN_DETECTED == True:
p=[('Human_Human',0.0)]*10 p=[('Human_Human',0.0)]*10
detections[str(pred_start) + ';' + str(pred_end)] = p detections[str(pred_start) + ';' + str(pred_end)] = p
@@ -226,17 +224,14 @@ def analyzeAudioData(chunks, lat, lon, week, sensitivity, overlap,):
# print('DETECTIONS:::::',detections) # print('DETECTIONS:::::',detections)
return detections return detections
def sendAppriseNotifications(species,confidence): def sendAppriseNotifications(species,confidence):
if os.path.exists(userDir + '/BirdNET-Pi/apprise.txt') and os.path.getsize(userDir + '/BirdNET-Pi/apprise.txt') > 0: if os.path.exists(userDir + '/BirdNET-Pi/apprise.txt') and os.path.getsize(userDir + '/BirdNET-Pi/apprise.txt') > 0:
with open(userDir + '/BirdNET-Pi/scripts/thisrun.txt', 'r') as f: with open(userDir + '/BirdNET-Pi/scripts/thisrun.txt', 'r') as f:
this_run = f.readlines() this_run = f.readlines()
title = str(str(str([i for i in this_run if i.startswith('APPRISE_NOTIFICATION_TITLE')] title = str(str(str([i for i in this_run if i.startswith('APPRISE_NOTIFICATION_TITLE')]).split('=')[1]).split('\\')[0]).replace('"', '')
).split('=')[1]).split('\\')[0]).replace('"', '') body = str(str(str([i for i in this_run if i.startswith('APPRISE_NOTIFICATION_BODY')]).split('=')[1]).split('\\')[0]).replace('"', '')
body = str(str(str([i for i in this_run if i.startswith('APPRISE_NOTIFICATION_BODY')]
).split('=')[1]).split('\\')[0]).replace('"', '')
if str(str(str([i for i in this_run if i.startswith('APPRISE_NOTIFY_EACH_DETECTION')]).split('=')[1]).split('\\')[0]) == "1": # noqa E501 if str(str(str([i for i in this_run if i.startswith('APPRISE_NOTIFY_EACH_DETECTION')]).split('=')[1]).split('\\')[0]) == "1":
apobj = apprise.Apprise() apobj = apprise.Apprise()
config = apprise.AppriseConfig() config = apprise.AppriseConfig()
@@ -244,17 +239,10 @@ def sendAppriseNotifications(species, confidence):
apobj.add(config) apobj.add(config)
apobj.notify( apobj.notify(
body=body.replace( body=body.replace("$sciname",species.split("_")[0]).replace("$comname",species.split("_")[1]).replace("$confidence",confidence),
"$sciname",
species.split("_")[0]).replace(
"$comname",
species.split("_")[1]).replace(
"$confidence",
confidence),
title=title, title=title,
) )
def writeResultsToFile(detections, min_conf, path): def writeResultsToFile(detections, min_conf, path):
print('WRITING RESULTS TO', path, '...', end=' ') print('WRITING RESULTS TO', path, '...', end=' ')
@@ -263,15 +251,13 @@ def writeResultsToFile(detections, min_conf, path):
rfile.write('Start (s);End (s);Scientific name;Common name;Confidence\n') rfile.write('Start (s);End (s);Scientific name;Common name;Confidence\n')
for d in detections: for d in detections:
for entry in detections[d]: for entry in detections[d]:
if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) ):
and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0)): sendAppriseNotifications(str(entry[0]),str(entry[1]));
sendAppriseNotifications(str(entry[0]), str(entry[1]))
rfile.write(d + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) + '\n') rfile.write(d + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) + '\n')
rcnt += 1 rcnt += 1
print('DONE! WROTE', rcnt, 'RESULTS.') print('DONE! WROTE', rcnt, 'RESULTS.')
return return
def handle_client(conn, addr): def handle_client(conn, addr):
global INCLUDE_LIST global INCLUDE_LIST
global EXCLUDE_LIST global EXCLUDE_LIST
@@ -302,6 +288,7 @@ def handle_client(conn, addr):
args.lat = -1 args.lat = -1
args.lon = -1 args.lon = -1
for line in msg.split('||'): for line in msg.split('||'):
inputvars = line.split('=') inputvars = line.split('=')
if inputvars[0] == 'i': if inputvars[0] == 'i':
@@ -327,6 +314,8 @@ def handle_client(conn, addr):
elif inputvars[0] == 'lon': elif inputvars[0] == 'lon':
args.lon = float(inputvars[1]) args.lon = float(inputvars[1])
# Load custom species lists - INCLUDED and EXCLUDED # Load custom species lists - INCLUDED and EXCLUDED
if not args.include_list == 'null': if not args.include_list == 'null':
INCLUDE_LIST = loadCustomSpeciesList(args.include_list) INCLUDE_LIST = loadCustomSpeciesList(args.include_list)
@@ -382,14 +371,13 @@ def handle_client(conn, addr):
for i in detections: for i in detections:
myReturn += str(i) + '-' + str(detections[i][0]) + '\n' myReturn += str(i) + '-' + str(detections[i][0]) + '\n'
with open(userDir + '/BirdNET-Pi/BirdDB.txt', 'a') as rfile: with open(userDir + '/BirdNET-Pi/BirdDB.txt', 'a') as rfile:
for d in detections: for d in detections:
for entry in detections[d]: for entry in detections[d]:
if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) and ( if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) ):
entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0)): rfile.write(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' \
rfile.write(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) +";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' \
+ str(entry[1]) + ";" + str(args.lat) + ';' + str(args.lon) +
';' + str(min_conf) + ';' + str(week) + ';'
+ str(args.sensitivity) +';' + str(args.overlap) + '\n') + str(args.sensitivity) +';' + str(args.overlap) + '\n')
Date = str(current_date) Date = str(current_date)
@@ -413,78 +401,34 @@ def handle_client(conn, addr):
try: try:
con = sqlite3.connect(userDir + '/BirdNET-Pi/scripts/birds.db') con = sqlite3.connect(userDir + '/BirdNET-Pi/scripts/birds.db')
cur = con.cursor() cur = con.cursor()
cur.execute( cur.execute("INSERT INTO detections VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (Date, Time, Sci_Name, Com_Name, str(score), Lat, Lon, Cutoff, Week, Sens, Overlap, File_Name))
"INSERT INTO detections VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(Date,
Time,
Sci_Name,
Com_Name,
str(score),
Lat,
Lon,
Cutoff,
Week,
Sens,
Overlap,
File_Name))
con.commit() con.commit()
con.close() con.close()
break break
except BaseException: except:
print("Database busy") print("Database busy")
time.sleep(2) time.sleep(2)
print(str(current_date) + print(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) + ';' + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' + str(args.sensitivity) +';' + str(args.overlap) + Com_Name.replace(" ", "_") + '-' + str(score) + '-' + str(current_date) + '-birdnet-' + str(current_time) + audiofmt + '\n')
';' +
str(current_time) +
';' +
entry[0].replace('_', ';') +
';' +
str(entry[1]) +
';' +
str(args.lat) +
';' +
str(args.lon) +
';' +
str(min_conf) +
';' +
str(week) +
';' +
str(args.sensitivity) +
';' +
str(args.overlap) +
Com_Name.replace(" ", "_") +
'-' +
str(score) +
'-' +
str(current_date) +
'-birdnet-' +
str(current_time) +
audiofmt +
'\n')
if birdweather_id != "99999": if birdweather_id != "99999":
try: try:
if soundscape_uploaded is False: if soundscape_uploaded is False:
# POST soundscape to server # POST soundscape to server
soundscape_url = "https://app.birdweather.com/api/v1/stations/" + \ soundscape_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/soundscapes" + "?timestamp=" + current_iso8601
birdweather_id + "/soundscapes" + "?timestamp=" + current_iso8601
with open(args.i, 'rb') as f: with open(args.i, 'rb') as f:
wav_data = f.read() wav_data = f.read()
response = requests.post( response = requests.post(url=soundscape_url, data=wav_data, headers={'Content-Type': 'application/octet-stream'})
url=soundscape_url, data=wav_data, headers={
'Content-Type': 'application/octet-stream'})
print("Soundscape POST Response Status - ", response.status_code) print("Soundscape POST Response Status - ", response.status_code)
sdata = response.json() sdata = response.json()
soundscape_id = sdata['soundscape']['id'] soundscape_id = sdata['soundscape']['id']
soundscape_uploaded = True soundscape_uploaded = True
# POST detection to server # POST detection to server
detection_url = "https://app.birdweather.com/api/v1/stations/" + \ detection_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/detections"
birdweather_id + "/detections"
start_time = d.split(';')[0] start_time = d.split(';')[0]
end_time = d.split(';')[1] end_time = d.split(';')[1]
post_begin = "{ " post_begin = "{ "
@@ -502,15 +446,11 @@ def handle_client(conn, addr):
post_confidence = "\"confidence\": " + str(entry[1]) post_confidence = "\"confidence\": " + str(entry[1])
post_end = " }" post_end = " }"
post_json = post_begin + \ post_json = post_begin + post_timestamp + post_lat + post_lon + post_soundscape_id + post_soundscape_start_time + post_soundscape_end_time + post_commonName + post_scientificName + post_algorithm + post_confidence + post_end
post_timestamp + post_lat + post_lon + \
post_soundscape_id + post_soundscape_start_time + \
post_soundscape_end_time + post_commonName + post_scientificName + \
post_algorithm + post_confidence + post_end
print(post_json) print(post_json)
response = requests.post(detection_url, json=json.loads(post_json)) response = requests.post(detection_url, json=json.loads(post_json))
print("Detection POST Response Status - ", response.status_code) print("Detection POST Response Status - ", response.status_code)
except BaseException: except:
print("Cannot POST right now") print("Cannot POST right now")
conn.send(myReturn.encode(FORMAT)) conn.send(myReturn.encode(FORMAT))
@@ -518,7 +458,6 @@ def handle_client(conn, addr):
conn.close() conn.close()
def start(): def start():
# Load model # Load model
global INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST global INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST