diff --git a/chirp/directory.py b/chirp/directory.py index 2e0695fe..0c029ee9 100644 --- a/chirp/directory.py +++ b/chirp/directory.py @@ -193,6 +193,12 @@ def __repr__(self): ex.metadata = metadata raise ex else: + # If we don't find anything else and the file appears to be a CSV + # file, then explicitly open it with the generic driver so we can + # get relevant errors instead of just "Unknown file format". + if image_file.lower().endswith('.csv'): + rclass = get_radio('Generic_CSV') + return rclass(image_file) raise errors.ImageDetectFailed("Unknown file format") diff --git a/chirp/drivers/generic_csv.py b/chirp/drivers/generic_csv.py index 82750652..e19e4f86 100644 --- a/chirp/drivers/generic_csv.py +++ b/chirp/drivers/generic_csv.py @@ -218,6 +218,10 @@ def _load(self, f): lineno += 1 if lineno == 1: header = line + for field in header: + # Log unknown header names for the UI to capture and expose + if field not in chirp_common.Memory.CSV_FORMAT: + LOG.error('Header line has unknown field %r' % field) self.file_has_rTone = "rToneFreq" in header self.file_has_cTone = "cToneFreq" in header continue @@ -348,7 +352,9 @@ def find_csv_header(filedata): filedata = filedata[1:] while filedata.startswith('#'): filedata = filedata[filedata.find('\n') + 1:] - return filedata.startswith('Location,') + delims = ['', '"', "'"] + return any([filedata.startswith('%sLocation%s,' % (d, d)) + for d in delims]) @directory.register diff --git a/chirp/wxui/common.py b/chirp/wxui/common.py index 33cce0ee..e735a561 100644 --- a/chirp/wxui/common.py +++ b/chirp/wxui/common.py @@ -22,7 +22,6 @@ import platform import shutil import tempfile -import textwrap import threading import wx @@ -711,8 +710,51 @@ def temporary_debug_log(): return dst +class MultiErrorDialog(wx.Dialog): + def __init__(self, *args, **kw): + super().__init__(*args, **kw) + vbox = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(vbox) + + self.choices = [] + self.choice_box = wx.ListBox(self) + vbox.Add(self.choice_box, border=10, proportion=0, + flag=wx.EXPAND | wx.ALL) + + self.message = wx.TextCtrl(self) + self.message.SetEditable(False) + vbox.Add(self.message, border=10, proportion=1, + flag=wx.EXPAND | wx.ALL) + + buttons = self.CreateButtonSizer(wx.OK) + vbox.Add(buttons, border=10, flag=wx.ALL) + + self.Bind(wx.EVT_BUTTON, self._button) + self.choice_box.Bind(wx.EVT_LISTBOX, self._selected) + + self.SetMinSize((600, 400)) + self.Fit() + self.Center() + + def _button(self, event): + self.EndModal(wx.ID_OK) + + def select(self, index): + error = self.choices[index] + self.message.SetValue('%s in %s:\n%s' % ( + error.levelname, error.module, error.getMessage())) + + def _selected(self, event): + self.select(event.GetInt()) + + def set_errors(self, errors): + self.choices = errors + self.choice_box.Set([x.getMessage() for x in self.choices]) + self.select(0) + + @contextlib.contextmanager -def expose_logs(level, root, label, maxlen=128): +def expose_logs(level, root, label, maxlen=128, parent=None): if not isinstance(root, tuple): root = (root,) @@ -725,9 +767,7 @@ def expose_logs(level, root, label, maxlen=128): lines = list(itertools.chain.from_iterable(x.get_history() for x in histories)) if lines: - msg = os.linesep.join(textwrap.shorten(x.getMessage(), maxlen) - for x in lines) - d = wx.MessageDialog( - None, str(msg), label, - style=wx.OK | wx.ICON_INFORMATION) + d = MultiErrorDialog(parent) + d.SetTitle(label) + d.set_errors(lines) d.ShowModal() diff --git a/chirp/wxui/main.py b/chirp/wxui/main.py index 81b481fa..54bb91a4 100644 --- a/chirp/wxui/main.py +++ b/chirp/wxui/main.py @@ -551,7 +551,8 @@ def open_file(self, filename, exists=True, select=True, rclass=None): self.enable_bugreport() CSVRadio = directory.get_radio('Generic_CSV') label = _('Driver messages') - with common.expose_logs(logging.WARNING, 'chirp.drivers', label): + with common.expose_logs(logging.WARNING, 'chirp.drivers', label, + parent=self): if exists: if not os.path.exists(filename): raise FileNotFoundError( @@ -1356,7 +1357,7 @@ def _menu_import(self, event): r = d.ShowModal() if r == wx.ID_YES: with common.expose_logs(logging.WARNING, 'chirp.drivers', - _('Import messages')): + _('Import messages'), parent=self): radio = directory.get_radio_by_image(filename) self.current_editorset.current_editor.memedit_import_all(radio) elif r == wx.ID_NO: diff --git a/tests/images/Generic_CSV.csv b/tests/images/Generic_CSV.csv index e50f849e..a595c068 100644 --- a/tests/images/Generic_CSV.csv +++ b/tests/images/Generic_CSV.csv @@ -1,104 +1,96 @@ -Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,Mode,TStep,Skip,Bank,Bank Index,URCALL,RPT1CALL,RPT2CALL -25,H-TAC1,443.100000,+,5.000000,DTCS,88.5,88.5,032,NN,FM,5.00,,,-1,,,, -26,H-TAC2,147.380000,+,0.600000,Tone,100.0,100.0,023,NN,FM,5.00,,,-1,,,, -27,H-TAC3,147.440000,,0.600000,Tone,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -28,H-TAC4,441.550000,+,5.000000,Tone,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -29,H-TAC5,442.925000,+,5.000000,Tone,107.2,107.2,023,NN,FM,5.00,,,-1,,,, -30,H-TAC6,443.350000,+,5.000000,Tone,156.7,156.7,023,NN,FM,5.00,,,-1,,,, -31,H-TAC7,442.825000,+,5.000000,,110.9,110.9,023,NN,FM,5.00,,,-1,,,, -50,ARESD1,147.320000,+,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -51,WAPRIR,146.900000,-,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -52,WAPRIS,147.400000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -53,WASECR,440.350000,+,5.000000,TSQL,127.3,127.3,023,NN,FM,5.00,,,-1,,,, -54,OEMNCS,145.330000,-,0.600000,Tone,186.2,186.2,023,NN,FM,5.00,,,-1,,,, -55,HEARTN,145.230000,-,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -56,CLACK,147.120000,+,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -57,CLATSP,146.660000,-,0.600000,Tone,118.8,118.8,023,NN,FM,5.00,,,-1,,,, -58,COLUMB,146.880000,-,0.600000,Tone,114.8,114.8,023,NN,FM,5.00,,,-1,,,, -59,TMOOK1,147.220000,+,0.600000,Tone,100.0,100.0,023,NN,FM,5.00,,,-1,,,, -60,TMOOK2,147.160000,+,0.600000,Tone,118.8,118.8,023,NN,FM,5.00,,,-1,,,, -61,TMOOK3,440.175000,+,5.000000,Tone,100.0,100.0,023,NN,FM,5.00,,,-1,,,, -62,TMOOK4,441.250000,+,5.000000,Tone,118.8,118.8,023,NN,FM,5.00,,,-1,,,, -63,MULTNM,146.840000,-,0.600000,Tone,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -64,CLARK,147.240000,+,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -65,ARC,146.980000,-,0.600000,Tone,123.0,123.0,023,NN,FM,5.00,,,-1,,,, -66,VERNIA,145.250000,-,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -80,WX1,162.400000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -81,WX2,162.425000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -82,WX3,162.450000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -83,WX4,162.475000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -84,WX5,162.500000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -85,WX6,162.525000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -86,WX7,162.550000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -88,CTAF,119.300000,,0.600000,,88.5,88.5,023,NN,AM,5.00,,,-1,,,, -89,ATIS,127.650000,,0.600000,,88.5,88.5,023,NN,AM,5.00,,,-1,,,, -90,GROUND,121.700000,,0.600000,,88.5,88.5,023,NN,AM,5.00,,,-1,,,, -91,TOWER,119.300000,,0.600000,,88.5,88.5,023,NN,AM,5.00,,,-1,,,, -92,UNICOM,122.950000,,0.600000,,88.5,88.5,023,NN,AM,5.00,,,-1,,,, -93,ACARS,131.550000,,0.600000,,88.5,88.5,023,NN,AM,5.00,,,-1,,,, -100,ICALL,851.012500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -101,ITAC1,851.512500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -102,ITAC2,852.012500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -103,ITAC3,852.512500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -104,ITAC4,853.012500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -105,OROPS1,851.325000,,0.600000,TSQL,88.5,156.7,023,NN,FM,10.00,,,-1,,,, -106,OROPS2,851.387500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -107,OROPS3,851.750000,,0.600000,TSQL,88.5,156.7,023,NN,FM,10.00,,,-1,,,, -108,OROPS4,851.775000,,0.600000,TSQL,88.5,156.7,023,NN,FM,10.00,,,-1,,,, -109,OROPS5,851.800000,,0.600000,TSQL,88.5,156.7,023,NN,FM,10.00,,,-1,,,, -110,WAOPS1,852.537500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -111,WAOPS2,852.562500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -112,WAOPS3,852.587500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -113,WAOPS4,852.612500,,0.600000,TSQL,88.5,156.7,023,NN,FM,10.00,,,-1,,,, -114,WAOPS5,852.637500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -115,UCAL40,453.212500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -116,UTAC41,453.462500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -117,UTAC42,453.712500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -118,UTAC43,453.862500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -119,ISIMP1,853.437500,,0.600000,DTCS,88.5,88.5,074,NN,FM,12.50,,,-1,,,, -120,ISIMP2,851.037500,,0.600000,DTCS,88.5,88.5,114,NN,FM,12.50,,,-1,,,, -121,ISIMP3,851.950000,,0.600000,DTCS,88.5,88.5,131,NN,FM,10.00,,,-1,,,, -122,ISIMP4,851.175000,,0.600000,DTCS,88.5,88.5,023,NN,FM,10.00,,,-1,,,, -123,MAYDAY,853.387500,,0.600000,DTCS,88.5,88.5,025,NN,FM,12.50,,,-1,,,, -124,VCALL,155.750000,,0.600000,TSQL,88.5,156.7,023,NN,FM,5.00,,,-1,,,, -125,VTAC11,151.137500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -126,VTAC12,154.452500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -127,VTAC13,158.737500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -128,VTAC14,159.472500,,0.600000,TSQL,88.5,156.7,023,NN,FM,12.50,,,-1,,,, -129,WCCCA1,860.737500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -130,WCCCA2,860.237500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -131,WCCCA3,859.737500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -132,WCCCA4,859.737500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -133,WCCCA5,858.237500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -134,WCCCA6,857.237500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -135,WCCCA7,856.237500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -136,WCCCA8,855.962500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -137,WCCCA9,855.237500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -138,WCCCA0,854.987500,,0.600000,,88.5,88.5,023,NN,FM,12.50,,,-1,,,, -139,OR SAR,155.805000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -140,OPEN,155.475000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -141,OSPTAC,156.030000,,0.600000,TSQL,88.5,156.7,023,NN,FM,5.00,,,-1,,,, -142,OSPD1A,154.935000,,0.600000,TSQL,88.5,179.9,023,NN,FM,5.00,,,-1,,,, -143,OSPD1B,156.225000,,0.600000,TSQL,88.5,179.9,023,NN,FM,5.00,,,-1,,,, -144,OSPD1C,154.905000,,0.600000,TSQL,88.5,179.9,023,NN,FM,5.00,,,-1,,,, -145,OSPD1D,156.150000,,0.600000,TSQL,88.5,179.9,023,NN,FM,5.00,,,-1,,,, -146,OSPD6A,153.935000,,0.600000,TSQL,88.5,156.7,023,NN,FM,5.00,,,-1,,,, -147,OSPD6B,154.785000,,0.600000,TSQL,88.5,156.7,023,NN,FM,5.00,,,-1,,,, -148,OSPD6C,154.860000,,0.600000,TSQL,88.5,131.8,023,NN,FM,5.00,,,-1,,,, -149,OSPD6D,155.910000,,0.600000,TSQL,88.5,131.8,023,NN,FM,5.00,,,-1,,,, -150,WASP,155.370000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -151,CHP,156.075000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -152,ODFW,158.895000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -153,SARINTOP,158.905000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -160,H-DS1,147.550000,,0.600000,,88.5,88.5,023,NN,DV,5.00,,,-1,CQCQCQ,,,0 -161,H-DS2,147.580000,,0.600000,,88.5,88.5,023,NN,DV,5.00,,,-1,CQCQCQ,,,0 -162,H-DS3,446.300000,,5.000000,,88.5,88.5,023,NN,DV,5.00,,,-1,CQCQCQ,,,0 -163,H-DS4,446.400000,,5.000000,,88.5,88.5,023,NN,DV,5.00,,,-1,CQCQCQ,,,0 -164,H-DS5,1294.100000,,0.600000,,88.5,88.5,023,NN,DV,5.00,,,-1,CQCQCQ,,,0 -165,H-DS6,441.637500,+,5.000000,,88.5,88.5,023,NN,DV,12.50,,,-1,CQCQCQ,,,0 -166,H-DS7,440.550000,+,5.000000,,88.5,88.5,023,NN,DV,5.00,,,-1,CQCQCQ,,,0 -167,H-DS8,444.262500,+,5.000000,,88.5,88.5,023,NN,DV,12.50,,,-1,CQCQCQ,,,0 -168,HPAGE,145.550000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -169,APRS,144.390000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -170,H-DAT1,145.550000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, -171,H-DAT2,145.070000,,0.600000,,88.5,88.5,023,NN,FM,5.00,,,-1,,,, +Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE +25,H-TAC1,443.100000,+,5.000000,DTCS,88.5,88.5,032,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +26,H-TAC2,147.380000,+,0.600000,Tone,100.0,100.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +27,H-TAC3,147.440000,,0.600000,Tone,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +28,H-TAC4,441.550000,+,5.000000,Tone,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +29,H-TAC5,442.925000,+,5.000000,Tone,107.2,107.2,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +30,H-TAC6,443.350000,+,5.000000,Tone,156.7,156.7,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +31,H-TAC7,442.825000,+,5.000000,,110.9,110.9,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +50,ARESD1,147.320000,+,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +51,WAPRIR,146.900000,-,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +52,WAPRIS,147.400000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +53,WASECR,440.350000,+,5.000000,TSQL,127.3,127.3,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +54,OEMNCS,145.330000,-,0.600000,Tone,186.2,186.2,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +55,HEARTN,145.230000,-,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +56,CLACK,147.120000,+,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +57,CLATSP,146.660000,-,0.600000,Tone,118.8,118.8,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +58,COLUMB,146.880000,-,0.600000,Tone,114.8,114.8,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +59,TMOOK1,147.220000,+,0.600000,Tone,100.0,100.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +60,TMOOK2,147.160000,+,0.600000,Tone,118.8,118.8,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +61,TMOOK3,440.175000,+,5.000000,Tone,100.0,100.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +62,TMOOK4,441.250000,+,5.000000,Tone,118.8,118.8,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +63,MULTNM,146.840000,-,0.600000,Tone,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +64,CLARK,147.240000,+,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +65,ARC,146.980000,-,0.600000,Tone,123.0,123.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +66,VERNIA,145.250000,-,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +80,WX1,162.400000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +81,WX2,162.425000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +82,WX3,162.450000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +83,WX4,162.475000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +84,WX5,162.500000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +85,WX6,162.525000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +86,WX7,162.550000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +88,CTAF,119.300000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,AM,5.00,,50W,,,,, +89,ATIS,127.650000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,AM,5.00,,50W,,,,, +90,GROUND,121.700000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,AM,5.00,,50W,,,,, +91,TOWER,119.300000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,AM,5.00,,50W,,,,, +92,UNICOM,122.950000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,AM,5.00,,50W,,,,, +93,ACARS,131.550000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,AM,5.00,,50W,,,,, +100,ICALL,851.012500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +101,ITAC1,851.512500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +102,ITAC2,852.012500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +103,ITAC3,852.512500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +104,ITAC4,853.012500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +105,OROPS1,851.325000,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,10.00,,50W,,,,, +106,OROPS2,851.387500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +107,OROPS3,851.750000,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,10.00,,50W,,,,, +108,OROPS4,851.775000,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,10.00,,50W,,,,, +109,OROPS5,851.800000,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,10.00,,50W,,,,, +110,WAOPS1,852.537500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +111,WAOPS2,852.562500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +112,WAOPS3,852.587500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +113,WAOPS4,852.612500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,10.00,,50W,,,,, +114,WAOPS5,852.637500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +115,UCAL40,453.212500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +116,UTAC41,453.462500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +117,UTAC42,453.712500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +118,UTAC43,453.862500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +119,ISIMP1,853.437500,,0.600000,DTCS,88.5,88.5,074,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +120,ISIMP2,851.037500,,0.600000,DTCS,88.5,88.5,114,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +121,ISIMP3,851.950000,,0.600000,DTCS,88.5,88.5,131,NN,023,Tone->Tone,FM,10.00,,50W,,,,, +122,ISIMP4,851.175000,,0.600000,DTCS,88.5,88.5,023,NN,023,Tone->Tone,FM,10.00,,50W,,,,, +123,MAYDAY,853.387500,,0.600000,DTCS,88.5,88.5,025,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +124,VCALL,155.750000,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +125,VTAC11,151.137500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +126,VTAC12,154.452500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +127,VTAC13,158.737500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +128,VTAC14,159.472500,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +129,WCCCA1,860.737500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +130,WCCCA2,860.237500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +131,WCCCA3,859.737500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +132,WCCCA4,859.737500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +133,WCCCA5,858.237500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +134,WCCCA6,857.237500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +135,WCCCA7,856.237500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +136,WCCCA8,855.962500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +137,WCCCA9,855.237500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +138,WCCCA0,854.987500,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,12.50,,50W,,,,, +139,OR SAR,155.805000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +140,OPEN,155.475000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +141,OSPTAC,156.030000,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +142,OSPD1A,154.935000,,0.600000,TSQL,88.5,179.9,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +143,OSPD1B,156.225000,,0.600000,TSQL,88.5,179.9,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +144,OSPD1C,154.905000,,0.600000,TSQL,88.5,179.9,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +145,OSPD1D,156.150000,,0.600000,TSQL,88.5,179.9,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +146,OSPD6A,153.935000,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +147,OSPD6B,154.785000,,0.600000,TSQL,88.5,156.7,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +148,OSPD6C,154.860000,,0.600000,TSQL,88.5,131.8,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +149,OSPD6D,155.910000,,0.600000,TSQL,88.5,131.8,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +150,WASP,155.370000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +151,CHP,156.075000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +152,ODFW,158.895000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +153,SARINTOP,158.905000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +168,HPAGE,145.550000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +169,APRS,144.390000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +170,H-DAT1,145.550000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, +171,H-DAT2,145.070000,,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, diff --git a/tests/unit/test_csv.py b/tests/unit/test_csv.py index 4f1980ba..60ee8cc5 100644 --- a/tests/unit/test_csv.py +++ b/tests/unit/test_csv.py @@ -23,6 +23,11 @@ 0,Nat Simplex,146.520000,,0.600000,TSQL,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,This is the national calling frequency on 2m,,,, 1,National Simp,446.000000,-,5.000000,DTCS,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,5.0W,This is NOT the UHF calling frequency,,,, """) # noqa +CHIRP_CSV_MODERN_QUOTED_HEADER = ( + """"Location","Name","Frequency","Duplex","Offset","Tone","rToneFreq","cToneFreq","DtcsCode","DtcsPolarity","RxDtcsCode","CrossMode","Mode","TStep","Skip","Power","Comment","URCALL","RPT1CALL","RPT2CALL","DVCODE" +0,Nat Simplex,146.520000,,0.600000,TSQL,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,This is the national calling frequency on 2m,,,, +1,"National Simp",446.000000,-,5.000000,DTCS,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,5.0W,This is NOT the UHF calling frequency,,,, +""") # noqa class TestCSV(unittest.TestCase): @@ -55,9 +60,9 @@ def test_parse_legacy(self): self.assertEqual('NFM', mem.mode) self.assertEqual(12.5, mem.tuning_step) - def test_parse_modern(self, output_encoding='utf-8'): + def test_parse_modern(self, output_encoding='utf-8', data=None): with open(self.testfn, 'w', encoding=output_encoding) as f: - f.write(CHIRP_CSV_MODERN) + f.write(data or CHIRP_CSV_MODERN) # Make sure we detect the file with open(self.testfn, 'rb') as f: self.assertTrue(generic_csv.CSVRadio.match_model( @@ -79,8 +84,8 @@ def test_parse_modern(self, output_encoding='utf-8'): self.assertEqual('5.0W', str(mem.power)) self.assertIn('UHF calling', mem.comment) - def test_csv_with_comments(self, output_encoding='utf-8'): - lines = list(CHIRP_CSV_MODERN.strip().split('\n')) + def _test_csv_with_comments(self, data, output_encoding='utf-8'): + lines = list(data.strip().split('\n')) lines.insert(0, '# This is a comment') lines.insert(0, '# Test file with comments') lines.insert(4, '# Test comment in the middle') @@ -99,7 +104,21 @@ def test_csv_with_comments(self, output_encoding='utf-8'): csv.save(self.testfn) with open(self.testfn, 'r') as f: read_lines = [x.strip() for x in f.readlines()] - self.assertEqual(lines, read_lines) + # Ignore quotes + self.assertEqual([x.replace('"', '') for x in lines], read_lines) + + def test_csv_with_comments(self): + self._test_csv_with_comments(CHIRP_CSV_MODERN) + + def test_csv_with_comments_quoted_header(self): + self._test_csv_with_comments(CHIRP_CSV_MODERN_QUOTED_HEADER) + + def test_parse_modern_quoted_header(self): + self.test_parse_modern(data=CHIRP_CSV_MODERN_QUOTED_HEADER) + + def test_parse_modern_quoted_header_bom(self): + self.test_parse_modern(output_encoding='utf-8-sig', + data=CHIRP_CSV_MODERN_QUOTED_HEADER) def test_parse_modern_bom(self): self.test_parse_modern(output_encoding='utf-8-sig')