Example #1
0
def upload_readings(transforms, meter_oid: int, scraper: str, task_id: str,
                    readings) -> Status:
    updated: List[MeterReading] = []
    if readings:
        readings = interval_transform.transform(transforms, task_id, scraper,
                                                meter_oid, readings)
        log.info("writing interval data to the database for %s %s", scraper,
                 meter_oid)
        updated = MeterReading.merge_readings(
            MeterReading.from_json(meter_oid, readings))

    if task_id and config.enabled("ES_INDEX_JOBS"):
        index.set_interval_fields(task_id, updated)

    log.info("Final Interval Summary")
    for when, intervals in readings.items():
        none_count = sum(1 for x in intervals if x is None)
        factor = (24 / len(intervals)) if len(intervals) > 0 else 1.0
        kWh = sum(x for x in intervals if x is not None) * factor
        log.info("%s: %d intervals. %.1f net kWh, %d null values." %
                 (when, len(intervals), kWh, none_count))

    path = os.path.join(config.WORKING_DIRECTORY, "readings.csv")
    with open(path, "w") as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(["Service", "Date", "Readings"])
        for when, intervals in readings.items():
            writer.writerow([meter_oid, str(when)] +
                            [str(x) for x in intervals])
    log.info("Wrote interval data to %s." % path)

    if updated:
        return Status.SUCCEEDED
    return Status.COMPLETED
Example #2
0
 def test_parse_readings(self):
     """Parse meter readings in scraper format into MeterReading objects."""
     dt = (datetime.today() - timedelta(days=3)).date()
     # data size doesn't match meter interval
     readings: Dict[str, List[float]] = {
         dt.strftime("%Y-%m-%d"): [1.0] * 24
     }
     with self.assertRaises(InvalidMeterDataException):
         MeterReading.from_json(self.meter_id, readings)
     # values are not floats
     readings: Dict[str, List[float]] = {
         dt.strftime("%Y-%m-%d"): ["1.0" * 96]
     }
     with self.assertRaises(InvalidMeterDataException):
         MeterReading.from_json(self.meter_id, readings)
     # valid data
     valid_dates: Set[date] = set()
     for idx in range(3):
         valid_dates.add(dt)
         # Check that both floating point and integer values are acceptable.
         readings[dt.strftime("%Y-%m-%d")] = [2.0] * 90 + [2] * 6
         dt += timedelta(days=1)
     # empty data ignored
     readings[dt.strftime("%Y-%m-%d")] = []
     readings[(dt + timedelta(days=1)).strftime("%Y-%m-%d")] = [None] * 96
     meter_readings: List[MeterReading] = MeterReading.from_json(
         self.meter_id, readings)
     self.assertEqual(3, len(meter_readings))
     for row in meter_readings:
         self.assertIn(row.occurred, valid_dates)
         self.assertEqual([2.0] * 96, row.readings)
         self.assertFalse(row.frozen)
Example #3
0
 def test_new_readings(self):
     """New readings can be added."""
     query = db.session.query(MeterReading).filter_by(meter=self.meter_id)
     self.assertEqual(7, query.count(), "7 readings from setup")
     dt = (datetime.today() - timedelta(days=3)).date()
     start_dt = dt
     readings: Dict[str, List[float]] = {}
     for idx in range(3):
         readings[dt.strftime("%Y-%m-%d")] = [2.0] * 96
         dt += timedelta(days=1)
     MeterReading.merge_readings(
         MeterReading.from_json(self.meter_id, readings))
     db.session.flush()
     self.assertEqual(10, query.count(), "10 readings after save")
     for row in query.filter(MeterReading.occurred < start_dt):
         self.assertEqual(
             96.0,
             sum(row.readings),
             "existing readings for %s unchanged" % row.occurred,
         )
         self.assertEqual(row.occurred, row.modified.date())
     for row in query.filter(MeterReading.occurred >= start_dt):
         self.assertEqual(96.0 * 2, sum(row.readings),
                          "new readings added for %s" % row.occurred)
         self.assertEqual(date.today(), row.modified.date())
Example #4
0
 def test_merge_readings(self):
     """New readings will be merged with old readings."""
     readings: Dict[str, List[float]] = {}
     # replace full row
     full_dt = date.today() - timedelta(days=10)
     readings[full_dt.strftime("%Y-%m-%d")] = [2.0] * 96
     # replace partial row
     partial_dt = date.today() - timedelta(days=9)
     readings[partial_dt.strftime("%Y-%m-%d")] = [None] * 90 + [2.0] * 6
     MeterReading.merge_readings(
         MeterReading.from_json(self.meter_id, readings))
     db.session.flush()
     query = db.session.query(MeterReading).filter_by(meter=self.meter_id)
     self.assertEqual(7, query.count(), "7 readings from setup")
     for row in query:
         if row.occurred == full_dt:
             self.assertEqual(96.0 * 2, sum(row.readings),
                              "%s fully replaced" % row.occurred)
             self.assertEqual(date.today(), row.modified.date())
         elif row.occurred == partial_dt:
             self.assertEqual(
                 90.0 + 12.0,
                 sum(row.readings),
                 "missing values for %s don't replace non-null values" %
                 row.occurred,
             )
             self.assertEqual(date.today(), row.modified.date())
         else:
             self.assertEqual(96.0, sum(row.readings))
             self.assertEqual(row.occurred, row.modified.date())
 def test_last_reading_date(self):
     page = MeterDataPage(None, None)
     start_date = date(2020, 5, 1)
     updated_start_date = page.start_date_from_readings(
         self.meter.oid, start_date)
     self.assertEqual(start_date, updated_start_date,
                      "no readings uses requested start date")
     # create a reading record older than start date
     reading_date = start_date - timedelta(days=3)
     db.session.add(
         MeterReading(meter=self.meter.oid,
                      occurred=reading_date,
                      readings=[1.0] * 96))
     db.session.flush()
     updated_start_date = page.start_date_from_readings(
         self.meter.oid, start_date)
     self.assertEqual(
         reading_date,
         updated_start_date,
         "use oldest reading date when older than start date",
     )
     reading_date = start_date + timedelta(days=3)
     # create a reading record newer than start date
     db.session.add(
         MeterReading(meter=self.meter.oid,
                      occurred=reading_date,
                      readings=[1.0] * 96))
     db.session.flush()
     updated_start_date = page.start_date_from_readings(
         self.meter.oid, start_date)
     self.assertEqual(
         start_date,
         updated_start_date,
         "use requested start date when newer readings exist",
     )
Example #6
0
 def test_set_interval_fields(self, index_etl_run):
     meter = self.meters[0]
     # no readings
     task_id = "abc123"
     index.set_interval_fields(task_id, [])
     self.assertEqual(
         {
             "updatedDays": 0,
         },
         index_etl_run.call_args[0][1],
     )
     # with users and readings
     today = date.today()
     dates = [
         today - timedelta(days=7),
         today - timedelta(days=6),
         today - timedelta(days=5),
     ]
     readings = []
     for dt in dates:
         readings.append(
             MeterReading(meter=meter.oid, occurred=dt,
                          readings=[1.0] * 96))
     index.set_interval_fields(task_id, readings)
     expected = {
         "updatedDays": 3,
         "intervalFrom": dates[0],
         "intervalTo": dates[-1],
         "age": 5,
     }
     self.assertEqual(expected, index_etl_run.call_args[0][1])
Example #7
0
 def test_frozen_readings(self):
     """New readings will not replace frozen readings."""
     query = (db.session.query(MeterReading).filter_by(
         meter=self.meter_id).order_by(MeterReading.occurred))
     reading = query.first()
     reading.frozen = True
     # if unchanged, SQLAlchemy will default to now
     reading.modified = datetime(reading.occurred.year,
                                 reading.occurred.month,
                                 reading.occurred.day, 1)
     db.session.add(reading)
     frozen_dt = reading.occurred
     dt = reading.occurred
     readings: Dict[str, List[float]] = {}
     for idx in range(3):
         readings[dt.strftime("%Y-%m-%d")] = [2.0] * 96
         dt += timedelta(days=1)
     latest_dt = dt
     MeterReading.merge_readings(
         MeterReading.from_json(self.meter_id, readings))
     db.session.flush()
     # first (frozen) reading unchanged
     reading = query.filter(MeterReading.occurred == frozen_dt).first()
     self.assertEqual(96.0, sum(reading.readings),
                      "frozen data not updated")
     self.assertEqual(reading.occurred, reading.modified.date(),
                      "frozen modified not updated")
     # other 2 readings updated
     for row in query.filter(MeterReading.occurred > reading.occurred):
         if row.occurred < latest_dt:
             self.assertEqual(
                 96.0 * 2,
                 sum(row.readings),
                 "readings for %s updated" % row.occurred,
             )
             self.assertEqual(date.today(), row.modified.date())
         else:
             self.assertEqual(96.0, sum(row.readings),
                              "readings for %s unchanged" % row.occurred)
             self.assertEqual(row.occurred, row.modified.date())
     self.assertEqual(7, query.count(), "7 readings from setup")
Example #8
0
 def setUp(self):
     meter = Meter(
         commodity="kw",
         interval=15,
         kind="main",
         name="Test Meter 1-%s" % datetime.now().strftime("%s"),
     )
     db.session.add(meter)
     db.session.flush()
     self.meter_id = meter.oid
     # create readings for a week ago
     dt = date.today() - timedelta(days=14)
     for idx in range(7):
         db.session.add(
             MeterReading(
                 meter=meter.oid,
                 occurred=dt,
                 readings=[1.0] * 96,
                 frozen=False,
                 modified=datetime(dt.year, dt.month, dt.day),
             ))
         dt += timedelta(days=1)
     db.session.flush()