# HG changeset patch # User Tom Hayward tom@tomh.us # Date 1484416953 28800 # Sat Jan 14 10:02:33 2017 -0800 # Node ID 2474f7b23e9123f4c76be4daaecd37d2284eaabf # Parent bcac2670345cfd5c1ea3cfb3e3b1022f5dac5f2d Add query/import from DMR-MARC repeater database. #4411
diff -r bcac2670345c -r 2474f7b23e91 chirp/chirp_common.py --- a/chirp/chirp_common.py Fri Jan 13 16:15:46 2017 -0800 +++ b/chirp/chirp_common.py Sat Jan 14 10:02:33 2017 -0800 @@ -70,7 +70,7 @@
MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY", "DIG", "PKT", "NCW", "NCWR", "CWR", "P25", "Auto", "RTTYR", - "FSK", "FSKR"] + "FSK", "FSKR", "DMR"]
TONE_MODES = [ "", diff -r bcac2670345c -r 2474f7b23e91 chirp/dmrmarc.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/dmrmarc.py Sat Jan 14 10:02:33 2017 -0800 @@ -0,0 +1,139 @@ +# Copyright 2016 Tom Hayward tom@tomh.us +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import json +import logging +import tempfile +import urllib +from chirp import chirp_common, errors +from chirp.settings import RadioSetting, RadioSettingGroup, \ + RadioSettingValueList + +LOG = logging.getLogger(__name__) + + +def list_filter(haystack, attr, needles): + if not needles or not needles[0]: + return haystack + return [x for x in haystack if x[attr] in needles] + + +class DMRMARCRadio(chirp_common.NetworkSourceRadio): + """DMR-MARC data source""" + VENDOR = "DMR-MARC" + MODEL = "Repeater database" + + URL = "http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?" \ + "table=repeaters&format=json" + + def __init__(self, *args, **kwargs): + chirp_common.NetworkSourceRadio.__init__(self, *args, **kwargs) + self._repeaters = None + + def set_params(self, city, state, country): + """Set the parameters to be used for a query""" + self._city = city and [x.strip() for x in city.split(",")] or [''] + self._state = state and [x.strip() for x in state.split(",")] or [''] + self._country = country and [x.strip() for x in country.split(",")] \ + or [''] + + def do_fetch(self): + fn = tempfile.mktemp(".json") + filename, headers = urllib.urlretrieve(self.URL, fn) + with open(fn, 'r') as f: + try: + self._repeaters = json.load(f)['repeaters'] + except AttributeError: + raise errors.RadioError( + "Unexpected response from %s" % self.URL) + except ValueError as e: + raise errors.RadioError( + "Invalid JSON from %s. %s" % (self.URL, str(e))) + + self._repeaters = list_filter(self._repeaters, "city", self._city) + self._repeaters = list_filter(self._repeaters, "state", self._state) + self._repeaters = list_filter(self._repeaters, "country", + self._country) + + def get_features(self): + if not self._repeaters: + self.do_fetch() + + rf = chirp_common.RadioFeatures() + rf.memory_bounds = (0, len(self._repeaters)-1) + rf.has_bank = False + rf.has_comment = True + rf.has_ctone = False + rf.valid_tmodes = [""] + return rf + + def get_raw_memory(self, number): + return repr(self._repeaters[number]) + + def get_memory(self, number): + if not self._repeaters: + self.do_fetch() + + repeater = self._repeaters[number] + + mem = chirp_common.Memory() + mem.number = number + + mem.name = repeater.get('city') + mem.freq = chirp_common.parse_freq(repeater.get('frequency')) + offset = chirp_common.parse_freq(repeater.get('offset', '0')) + if offset > 0: + mem.duplex = "+" + elif offset < 0: + mem.duplex = "-" + else: + mem.duplex = "" + mem.offset = abs(offset) + mem.mode = 'DMR' + mem.comment = repeater.get('map_info') + + mem.extra = RadioSettingGroup("Extra", "extra") + + rs = RadioSetting( + "color_code", "Color Code", RadioSettingValueList( + range(16), int(repeater.get('color_code', 0)))) + mem.extra.append(rs) + + return mem + + +def main(): + import argparse + from pprint import PrettyPrinter + + parser = argparse.ArgumentParser(description="Fetch DMR-MARC repeater " + "database and filter by city, state, and/or country. Multiple items " + "combined with a , will be filtered with logical OR.") + parser.add_argument("-c", "--city", + help="Comma-separated list of cities to include in output.") + parser.add_argument("-s", "--state", + help="Comma-separated list of states to include in output.") + parser.add_argument("--country", + help="Comma-separated list of countries to include in output.") + args = parser.parse_args() + + dmrmarc = DMRMARCRadio(None) + dmrmarc.set_params(**vars(args)) + dmrmarc.do_fetch() + pp = PrettyPrinter(indent=2) + pp.pprint(dmrmarc._repeaters) + +if __name__ == "__main__": + main() diff -r bcac2670345c -r 2474f7b23e91 chirp/ui/mainapp.py --- a/chirp/ui/mainapp.py Fri Jan 13 16:15:46 2017 -0800 +++ b/chirp/ui/mainapp.py Sat Jan 14 10:02:33 2017 -0800 @@ -809,6 +809,65 @@ count = eset.do_import(filen) reporting.report_model_usage(eset.rthread.radio, "import", count > 0)
+ def do_dmrmarc_prompt(self): + fields = {"1City": (gtk.Entry(), lambda x: x), + "2State": (gtk.Entry(), lambda x: x), + "3Country": (gtk.Entry(), lambda x: x), + } + + d = inputdialog.FieldDialog(title=_("DMR-MARC Repeater Database Dump"), + parent=self) + for k in sorted(fields.keys()): + d.add_field(k[1:], fields[k][0]) + fields[k][0].set_text(CONF.get(k[1:], "dmrmarc") or "") + + while d.run() == gtk.RESPONSE_OK: + for k in sorted(fields.keys()): + widget, validator = fields[k] + try: + if validator(widget.get_text()): + CONF.set(k[1:], widget.get_text(), "dmrmarc") + continue + except Exception: + pass + + d.destroy() + return True + + d.destroy() + return False + + def do_dmrmarc(self, do_import): + self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) + if not self.do_dmrmarc_prompt(): + self.window.set_cursor(None) + return + + city = CONF.get("city", "dmrmarc") + state = CONF.get("state", "dmrmarc") + country = CONF.get("country", "dmrmarc") + + # Do this in case the import process is going to take a while + # to make sure we process events leading up to this + gtk.gdk.window_process_all_updates() + while gtk.events_pending(): + gtk.main_iteration(False) + + if do_import: + eset = self.get_current_editorset() + dmrmarcstr = "dmrmarc://%s/%s/%s" % (city, state, country) + eset.do_import(dmrmarcstr) + else: + try: + from chirp import dmrmarc + radio = dmrmarc.DMRMARCRadio(None) + radio.set_params(city, state, country) + self.do_open_live(radio, read_only=True) + except errors.RadioError, e: + common.show_error(e) + + self.window.set_cursor(None) + def do_repeaterbook_prompt(self): if not CONF.get_bool("has_seen_credit", "repeaterbook"): d = gtk.MessageDialog(parent=self, buttons=gtk.BUTTONS_OK) @@ -1439,6 +1498,8 @@ self.do_close() elif action == "import": self.do_import() + elif action in ["qdmrmarc", "idmrmarc"]: + self.do_dmrmarc(action[0] == "i") elif action in ["qrfinder", "irfinder"]: self.do_rfinder(action[0] == "i") elif action in ["qradioreference", "iradioreference"]: @@ -1534,12 +1595,14 @@ <menuitem action="download"/> <menuitem action="upload"/> <menu action="importsrc" name="importsrc"> + <menuitem action="idmrmarc"/> <menuitem action="iradioreference"/> <menuitem action="irbook"/> <menuitem action="ipr"/> <menuitem action="irfinder"/> </menu> <menu action="querysrc" name="querysrc"> + <menuitem action="qdmrmarc"/> <menuitem action="qradioreference"/> <menuitem action="qrbook"/> <menuitem action="qpr"/> @@ -1612,12 +1675,14 @@ ('export', None, _("Export"), "%se" % ALT_KEY, None, self.mh), ('importsrc', None, _("Import from data source"), None, None, self.mh), + ('idmrmarc', None, _("DMR-MARC Repeaters"), None, None, self.mh), ('iradioreference', None, _("RadioReference.com"), None, None, self.mh), ('irfinder', None, _("RFinder"), None, None, self.mh), ('irbook', None, _("RepeaterBook"), None, None, self.mh), ('ipr', None, _("przemienniki.net"), None, None, self.mh), ('querysrc', None, _("Query data source"), None, None, self.mh), + ('qdmrmarc', None, _("DMR-MARC Repeaters"), None, None, self.mh), ('qradioreference', None, _("RadioReference.com"), None, None, self.mh), ('qrfinder', None, _("RFinder"), None, None, self.mh),