{"sha":"5ed771b6927af5612f38289533237f150aae2853","node_id":"MDY6Q29tbWl0NDgzMDU3NTQ6NWVkNzcxYjY5MjdhZjU2MTJmMzgyODk1MzMyMzdmMTUwYWFlMjg1Mw==","commit":{"author":{"name":"Chris J. Karr","email":"chris@audacious-software.com","date":"2019-03-31T03:49:24Z"},"committer":{"name":"Chris J. Karr","email":"chris@audacious-software.com","date":"2019-03-31T03:49:24Z"},"message":"Misc. bug fixes.\n\n* Added ability for custom exporters to specify child generators.","tree":{"sha":"56065332150394bf640216a49521d2a07bc7c08c","url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/git/trees/56065332150394bf640216a49521d2a07bc7c08c"},"url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/git/commits/5ed771b6927af5612f38289533237f150aae2853","comment_count":0,"verification":{"verified":false,"reason":"unsigned","signature":null,"payload":null}},"url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/commits/5ed771b6927af5612f38289533237f150aae2853","html_url":"https://github.com/audacious-software/PassiveDataKit-Django/commit/5ed771b6927af5612f38289533237f150aae2853","comments_url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/commits/5ed771b6927af5612f38289533237f150aae2853/comments","author":{"login":"audaciouscode","id":1141048,"node_id":"MDQ6VXNlcjExNDEwNDg=","avatar_url":"https://avatars.githubusercontent.com/u/1141048?v=4","gravatar_id":"","url":"https://api.github.com/users/audaciouscode","html_url":"https://github.com/audaciouscode","followers_url":"https://api.github.com/users/audaciouscode/followers","following_url":"https://api.github.com/users/audaciouscode/following{/other_user}","gists_url":"https://api.github.com/users/audaciouscode/gists{/gist_id}","starred_url":"https://api.github.com/users/audaciouscode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/audaciouscode/subscriptions","organizations_url":"https://api.github.com/users/audaciouscode/orgs","repos_url":"https://api.github.com/users/audaciouscode/repos","events_url":"https://api.github.com/users/audaciouscode/events{/privacy}","received_events_url":"https://api.github.com/users/audaciouscode/received_events","type":"User","site_admin":false},"committer":{"login":"audaciouscode","id":1141048,"node_id":"MDQ6VXNlcjExNDEwNDg=","avatar_url":"https://avatars.githubusercontent.com/u/1141048?v=4","gravatar_id":"","url":"https://api.github.com/users/audaciouscode","html_url":"https://github.com/audaciouscode","followers_url":"https://api.github.com/users/audaciouscode/followers","following_url":"https://api.github.com/users/audaciouscode/following{/other_user}","gists_url":"https://api.github.com/users/audaciouscode/gists{/gist_id}","starred_url":"https://api.github.com/users/audaciouscode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/audaciouscode/subscriptions","organizations_url":"https://api.github.com/users/audaciouscode/orgs","repos_url":"https://api.github.com/users/audaciouscode/repos","events_url":"https://api.github.com/users/audaciouscode/events{/privacy}","received_events_url":"https://api.github.com/users/audaciouscode/received_events","type":"User","site_admin":false},"parents":[{"sha":"fbf1d8ebe62cd8ec9151a5027bd0db58ef988d12","url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/commits/fbf1d8ebe62cd8ec9151a5027bd0db58ef988d12","html_url":"https://github.com/audacious-software/PassiveDataKit-Django/commit/fbf1d8ebe62cd8ec9151a5027bd0db58ef988d12"}],"stats":{"total":145,"additions":123,"deletions":22},"files":[{"sha":"69c7cec23e8bcb662b2053d9337ea17b79d96f78","filename":"admin.py","status":"modified","additions":9,"deletions":2,"changes":11,"blob_url":"https://github.com/audacious-software/PassiveDataKit-Django/blob/5ed771b6927af5612f38289533237f150aae2853/admin.py","raw_url":"https://github.com/audacious-software/PassiveDataKit-Django/raw/5ed771b6927af5612f38289533237f150aae2853/admin.py","contents_url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/contents/admin.py?ref=5ed771b6927af5612f38289533237f150aae2853","patch":"@@ -1,4 +1,4 @@\n-# pylint: disable=no-member\n+# pylint: disable=no-member, line-too-long\n \n import datetime\n import json\n@@ -12,7 +12,7 @@\n from .models import DataPoint, DataBundle, DataSource, DataSourceGroup, \\\n DataPointVisualization, ReportJob, DataSourceAlert, \\\n DataServerMetadatum, ReportJobBatchRequest, DataServerApiToken, \\\n- DataFile\n+ DataFile, AppConfiguration\n \n def reset_visualizations(modeladmin, request, queryset): # pylint: disable=unused-argument\n for visualization in queryset:\n@@ -196,3 +196,10 @@ class DataSourceAlertAdmin(admin.OSMGeoAdmin):\n class DataServerApiTokenAdmin(admin.OSMGeoAdmin):\n list_display = ('user', 'expires',)\n list_filter = ('expires', 'user',)\n+\n+@admin.register(AppConfiguration)\n+class AppConfigurationAdmin(admin.OSMGeoAdmin):\n+ list_display = ('name', 'evaluate_order', 'id_pattern', 'context_pattern', 'is_valid', 'is_enabled',)\n+ search_fields = ('name', 'id_pattern', 'context_pattern', 'configuration_json',)\n+\n+ list_filter = ('is_enabled', 'is_valid',)"},{"sha":"28e91af4302655e5e53b9927a27f2b1a041095d0","filename":"management/commands/pdk_update_server_health.py","status":"modified","additions":4,"deletions":4,"changes":8,"blob_url":"https://github.com/audacious-software/PassiveDataKit-Django/blob/5ed771b6927af5612f38289533237f150aae2853/management%2Fcommands%2Fpdk_update_server_health.py","raw_url":"https://github.com/audacious-software/PassiveDataKit-Django/raw/5ed771b6927af5612f38289533237f150aae2853/management%2Fcommands%2Fpdk_update_server_health.py","contents_url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/contents/management%2Fcommands%2Fpdk_update_server_health.py?ref=5ed771b6927af5612f38289533237f150aae2853","patch":"@@ -1,8 +1,8 @@\n # pylint: disable=no-member,line-too-long\n \n+import calendar\n import datetime\n import json\n-import time\n \n from django.core.management.base import BaseCommand\n from django.utils import timezone\n@@ -39,7 +39,7 @@ def handle(self, *args, **options): # pylint: disable=too-many-branches\n data['last_bundle_count'] = bundle_count\n \n data['bundle_snapshots'].append({\n- 'time': time.mktime(now.timetuple()),\n+ 'time': calendar.timegm(now.utctimetuple()),\n 'unprocessed': unprocessed_count,\n 'added': bundle_count - data['last_bundle_count']\n })\n@@ -55,13 +55,13 @@ def handle(self, *args, **options): # pylint: disable=too-many-branches\n data['last_point_count'] = point_count\n \n data['point_snapshots'].append({\n- 'time': time.mktime(now.timetuple()),\n+ 'time': calendar.timegm(now.utctimetuple()),\n 'added': point_count - data['last_point_count']\n })\n \n data['last_point_count'] = point_count\n \n- start_ts = time.mktime(start.timetuple())\n+ start_ts = calendar.timegm(start.utctimetuple())\n \n to_delete = []\n "},{"sha":"a23979186afb8b8e0bafbe34fa4826885e25d80e","filename":"migrations/0045_auto_20190225_1821.py","status":"added","additions":41,"deletions":0,"changes":41,"blob_url":"https://github.com/audacious-software/PassiveDataKit-Django/blob/5ed771b6927af5612f38289533237f150aae2853/migrations%2F0045_auto_20190225_1821.py","raw_url":"https://github.com/audacious-software/PassiveDataKit-Django/raw/5ed771b6927af5612f38289533237f150aae2853/migrations%2F0045_auto_20190225_1821.py","contents_url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/contents/migrations%2F0045_auto_20190225_1821.py?ref=5ed771b6927af5612f38289533237f150aae2853","patch":"@@ -0,0 +1,41 @@\n+# pylint: skip-file\n+# -*- coding: utf-8 -*-\n+# Generated by Django 1.11.20 on 2019-02-25 18:21\n+from __future__ import unicode_literals\n+\n+import django.contrib.postgres.fields.jsonb\n+from django.db import migrations, models\n+import django.db.models.deletion\n+\n+\n+class Migration(migrations.Migration):\n+\n+ dependencies = [\n+ ('passive_data_kit', '0044_dataserverapitoken'),\n+ ]\n+\n+ operations = [\n+ migrations.CreateModel(\n+ name='AppConfiguration',\n+ fields=[\n+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n+ ('name', models.CharField(max_length=1024)),\n+ ('id_pattern', models.CharField(max_length=1024)),\n+ ('context_pattern', models.CharField(default='.*', max_length=1024)),\n+ ('configuration_json', django.contrib.postgres.fields.jsonb.JSONField()),\n+ ('evaluate_order', models.IntegerField(default=1)),\n+ ('is_valid', models.BooleanField(default=False)),\n+ ('is_enabled', models.BooleanField(default=True)),\n+ ],\n+ ),\n+ migrations.AlterField(\n+ model_name='datafile',\n+ name='data_bundle',\n+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='data_files', to='passive_data_kit.DataBundle'),\n+ ),\n+ migrations.AlterField(\n+ model_name='datafile',\n+ name='data_point',\n+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='data_files', to='passive_data_kit.DataPoint'),\n+ ),\n+ ]"},{"sha":"32c8af9ae89fc93d60206db8f4f56d51d8e4604b","filename":"models.py","status":"modified","additions":42,"deletions":10,"changes":52,"blob_url":"https://github.com/audacious-software/PassiveDataKit-Django/blob/5ed771b6927af5612f38289533237f150aae2853/models.py","raw_url":"https://github.com/audacious-software/PassiveDataKit-Django/raw/5ed771b6927af5612f38289533237f150aae2853/models.py","contents_url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/contents/models.py?ref=5ed771b6927af5612f38289533237f150aae2853","patch":"@@ -698,9 +698,25 @@ def process(self): # pylint: disable=too-many-locals, too-many-branches, too-man\n else:\n generator_query = generator_query | Q(generator_identifier=generator)\n \n+ for app in settings.INSTALLED_APPS:\n+ try:\n+ pdk_api = importlib.import_module(app + '.pdk_api')\n+\n+ try:\n+ other_generators = pdk_api.generators_for_extra_generator(generator)\n+\n+ for other_generator in other_generators:\n+ generator_query = generator_query | Q(generator_identifier=other_generator)\n+ except TypeError as exception:\n+ print 'Verify that ' + app + '.' + generator + ' implements all generators_for_extra_generator arguments!'\n+ raise exception\n+ except ImportError:\n+ pass\n+ except AttributeError:\n+ pass\n+\n requested = timezone.now()\n \n- source_query = None\n report_size = 0\n report_sources = []\n \n@@ -711,10 +727,7 @@ def process(self): # pylint: disable=too-many-locals, too-many-branches, too-man\n while sources:\n source = sources.pop()\n \n- if source_query is None:\n- source_query = Q(source=source)\n- else:\n- source_query = source_query | Q(source=source)\n+ source_query = Q(source=source)\n \n query_size = DataPoint.objects.filter(generator_query, source_query).count()\n \n@@ -740,12 +753,10 @@ def process(self): # pylint: disable=too-many-locals, too-many-branches, too-man\n \n pending_jobs.append(job)\n \n- source_query = None\n- report_size = 0\n- report_sources = []\n+ report_size = query_size\n+ report_sources = [source]\n \n-\n- if report_sources and source_query is not None:\n+ if report_sources:\n job = ReportJob(requester=self.requester, requested=requested)\n \n job_params = {}\n@@ -797,3 +808,24 @@ def fetch_token(self):\n self.save()\n \n return self.token\n+\n+class AppConfiguration(models.Model):\n+ name = models.CharField(max_length=1024)\n+ id_pattern = models.CharField(max_length=1024)\n+ context_pattern = models.CharField(max_length=1024, default='.*')\n+\n+ if install_supports_jsonfield():\n+ configuration_json = JSONField()\n+ else:\n+ configuration_json = models.TextField(max_length=(32 * 1024 * 1024 * 1024))\n+\n+ evaluate_order = models.IntegerField(default=1)\n+\n+ is_valid = models.BooleanField(default=False)\n+ is_enabled = models.BooleanField(default=True)\n+\n+ def configuration(self):\n+ if install_supports_jsonfield():\n+ return self.configuration_json\n+\n+ return json.loads(self.configuration_json)"},{"sha":"00444d077a89d724631ce4013e51b08014459cc2","filename":"templates/pdk_base.html","status":"modified","additions":1,"deletions":1,"changes":2,"blob_url":"https://github.com/audacious-software/PassiveDataKit-Django/blob/5ed771b6927af5612f38289533237f150aae2853/templates%2Fpdk_base.html","raw_url":"https://github.com/audacious-software/PassiveDataKit-Django/raw/5ed771b6927af5612f38289533237f150aae2853/templates%2Fpdk_base.html","contents_url":"https://api.github.com/repos/audacious-software/PassiveDataKit-Django/contents/templates%2Fpdk_base.html?ref=5ed771b6927af5612f38289533237f150aae2853","patch":"@@ -8,7 +8,7 @@\n \t\t\n \t\t\n \t\t\n-\t\t