[chirp_devel] [PATCH] Add query/import from DMR-MARC repeater database. #4411

Tom Hayward
Sat Jan 14 10:02:36 PST 2017


# HG changeset patch
# User Tom Hayward <tom at 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 at 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),



More information about the chirp_devel mailing list