diff --git a/case/Z2/LPM_Case_Z2_Base_Flange.blend b/case/Z2/LPM_Case_Z2_Base_Flange.blend index f3e3f3a..ca454bf 100644 Binary files a/case/Z2/LPM_Case_Z2_Base_Flange.blend and b/case/Z2/LPM_Case_Z2_Base_Flange.blend differ diff --git a/case/Z2/LPM_Case_Z2_Base_Flange.stl b/case/Z2/LPM_Case_Z2_Base_Flange.stl index 80263ba..244d3ac 100644 Binary files a/case/Z2/LPM_Case_Z2_Base_Flange.stl and b/case/Z2/LPM_Case_Z2_Base_Flange.stl differ diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/led/LEDFlasher.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/led/LEDFlasher.java index e7a5475..c26de9b 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/led/LEDFlasher.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/led/LEDFlasher.java @@ -28,10 +28,14 @@ public class LEDFlasher implements Runnable { public static void setLEDOn(boolean _on) { try { - if (_on) + if (_on) { + Runtime.getRuntime().exec(new String[]{"sh", "-c", "echo default-on > /sys/class/leds/led0/trigger"}); Runtime.getRuntime().exec(new String[]{"sh", "-c", "echo default-on > /sys/class/leds/led1/trigger"}); - else + } + else { + Runtime.getRuntime().exec(new String[]{"sh", "-c", "echo none > /sys/class/leds/led0/trigger"}); Runtime.getRuntime().exec(new String[]{"sh", "-c", "echo none > /sys/class/leds/led1/trigger"}); + } } catch (Exception _e) { LOG.error("Failed to change LED state", _e); diff --git a/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/CreateConfig.java b/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/CreateConfig.java index cd9b95e..6005d9d 100644 --- a/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/CreateConfig.java +++ b/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/CreateConfig.java @@ -3,7 +3,7 @@ package com.lanternsoftware.currentmonitor; import com.lanternsoftware.datamodel.currentmonitor.Breaker; import com.lanternsoftware.util.CollectionUtils; -import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.dao.DaoSerializer; @@ -23,6 +23,6 @@ public class CreateConfig { b1.setPort(1); b1.setSizeAmps(20); c.setMqttBreakers(CollectionUtils.asArrayList(b1)); - ResourceLoader.writeFile(LanternFiles.OPS_PATH + "mqtt1.json", DaoSerializer.toJson(c)); + ResourceLoader.writeFile(LanternFiles.CONFIG_PATH + "mqtt1.json", DaoSerializer.toJson(c)); } } diff --git a/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/CurrentMonitorAppSerializers.java b/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/CurrentMonitorAppSerializers.java index e7dc8fb..0dda6c1 100644 --- a/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/CurrentMonitorAppSerializers.java +++ b/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/CurrentMonitorAppSerializers.java @@ -1,11 +1,11 @@ package com.lanternsoftware.currentmonitor; -import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.dao.generator.DaoSerializerGenerator; public class CurrentMonitorAppSerializers { public static void main(String[] args) { - DaoSerializerGenerator.generateSerializers(LanternFiles.SOURCE_PATH + "currentmonitor", true, null); + DaoSerializerGenerator.generateSerializers(LanternFiles.SOURCE_CODE_PATH + "currentmonitor", true, null); } } diff --git a/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/ReleaseCurrentMonitor.java b/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/ReleaseCurrentMonitor.java index bd3efdb..521f0fe 100644 --- a/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/ReleaseCurrentMonitor.java +++ b/currentmonitor/lantern-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/ReleaseCurrentMonitor.java @@ -1,6 +1,6 @@ package com.lanternsoftware.currentmonitor; -import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.dao.DaoEntity; import com.lanternsoftware.util.dao.DaoSerializer; @@ -9,18 +9,17 @@ import com.lanternsoftware.util.xml.XmlParser; import org.apache.commons.codec.digest.DigestUtils; import java.io.File; -import java.util.Arrays; import java.util.Collections; public class ReleaseCurrentMonitor { public static void main(String[] args) { - XmlNode pom = XmlParser.loadXmlFile(LanternFiles.SOURCE_PATH + "currentmonitor" + File.separator + "lantern-currentmonitor" + File.separator + "pom.xml"); + XmlNode pom = XmlParser.loadXmlFile(LanternFiles.SOURCE_CODE_PATH + "currentmonitor" + File.separator + "lantern-currentmonitor" + File.separator + "pom.xml"); if (pom == null) return; XmlNode versionNode = pom.getChild(Collections.singletonList("version")); String version = versionNode.getContent(); ProcessBuilder builder = new ProcessBuilder(); - builder.directory(new File(LanternFiles.SOURCE_PATH)); + builder.directory(new File(LanternFiles.SOURCE_CODE_PATH)); builder.command("cmd.exe", "/c", "mvn clean install"); builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); try { @@ -30,9 +29,9 @@ public class ReleaseCurrentMonitor { } catch (Exception _e) { _e.printStackTrace(); } - byte[] jar = ResourceLoader.loadFile(LanternFiles.SOURCE_PATH + "currentmonitor" + File.separator + "lantern-currentmonitor" + File.separator + "target" + File.separator + "lantern-currentmonitor.jar"); + byte[] jar = ResourceLoader.loadFile(LanternFiles.SOURCE_CODE_PATH + "currentmonitor" + File.separator + "lantern-currentmonitor" + File.separator + "target" + File.separator + "lantern-currentmonitor.jar"); DaoEntity meta = new DaoEntity("version", version).and("size", jar.length).and("checksum", DigestUtils.md5Hex(jar)); - ResourceLoader.writeFile(LanternFiles.OPS_PATH + "release" + File.separator + "lantern-currentmonitor.jar", jar); - ResourceLoader.writeFile(LanternFiles.OPS_PATH + "release" + File.separator + "version.json", DaoSerializer.toJson(meta)); + ResourceLoader.writeFile(LanternFiles.CONFIG_PATH + "release" + File.separator + "lantern-currentmonitor.jar", jar); + ResourceLoader.writeFile(LanternFiles.CONFIG_PATH + "release" + File.separator + "version.json", DaoSerializer.toJson(meta)); } } diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/Backup.java b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/Backup.java index a382bdb..639b5fa 100644 --- a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/Backup.java +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/Backup.java @@ -11,7 +11,7 @@ import com.lanternsoftware.datamodel.rules.Event; import com.lanternsoftware.datamodel.rules.FcmDevice; import com.lanternsoftware.datamodel.rules.Rule; import com.lanternsoftware.util.DebugTimer; -import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.dao.DaoQuery; import com.lanternsoftware.util.dao.mongo.MongoConfig; @@ -19,8 +19,8 @@ import java.util.List; public class Backup { public static void main(String[] args) { - CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_SOURCE + "mongo.cfg")); - CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST + "mongo.cfg")); + CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg")); + CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST_PATH + "mongo.cfg")); DebugTimer t1 = new DebugTimer("Query Accounts"); List accounts = dao.getProxy().queryAll(Account.class); diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/BackupMinutes.java b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/BackupMinutes.java index 2565504..cc35958 100644 --- a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/BackupMinutes.java +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/BackupMinutes.java @@ -4,7 +4,7 @@ import com.lanternsoftware.datamodel.currentmonitor.Account; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.util.DateUtils; import com.lanternsoftware.util.DebugTimer; -import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.dao.DaoQuery; import com.lanternsoftware.util.dao.DaoSort; @@ -16,8 +16,8 @@ import java.util.TimeZone; public class BackupMinutes { public static void main(String[] args) { - CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_SOURCE + "mongo.cfg")); - CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST + "mongo.cfg")); + CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg")); + CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST_PATH + "mongo.cfg")); Date now = new Date(); for (Account a : dao.getProxy().queryAll(Account.class)) { if (a.getId() == 0) diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/CurrentMonitorDao.java b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/CurrentMonitorDao.java index 6c8171d..7f60be1 100644 --- a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/CurrentMonitorDao.java +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/CurrentMonitorDao.java @@ -7,9 +7,12 @@ import com.lanternsoftware.datamodel.currentmonitor.EnergySummary; import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode; import com.lanternsoftware.datamodel.currentmonitor.HubCommand; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; +import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus; +import com.lanternsoftware.util.DateRange; import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.mongo.MongoProxy; +import java.io.InputStream; import java.util.Date; import java.util.List; import java.util.TimeZone; @@ -26,6 +29,15 @@ public interface CurrentMonitorDao { void putEnergySummary(EnergySummary _energy); void putHubPowerMinute(HubPowerMinute _power); + Iterable streamHubPowerMinutes(int _accountId, Date _start, Date _end); + + void archiveMonth(int _accountId, Date _month); + InputStream streamArchive(int _accountId, Date _month); + void putArchiveStatus(ArchiveStatus _status); + void deleteArchiveStatus(int _accountId, Date _month); + List getArchiveStatus(int _accountId); + + DateRange getMonitoredDateRange(int _accountId); BreakerConfig getConfig(int _accountId); BreakerConfig getMergedConfig(AuthCode _authCode); diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java index ad17f4f..779b8f1 100644 --- a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java @@ -2,12 +2,13 @@ package com.lanternsoftware.dataaccess.currentmonitor; import com.lanternsoftware.datamodel.currentmonitor.Account; import com.lanternsoftware.datamodel.currentmonitor.BillingPlan; -import com.lanternsoftware.datamodel.currentmonitor.BillingRate; import com.lanternsoftware.datamodel.currentmonitor.Breaker; import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig; import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup; import com.lanternsoftware.datamodel.currentmonitor.BreakerHub; import com.lanternsoftware.datamodel.currentmonitor.BreakerPower; +import com.lanternsoftware.datamodel.currentmonitor.BreakerPowerMinute; +import com.lanternsoftware.datamodel.currentmonitor.BreakerType; import com.lanternsoftware.datamodel.currentmonitor.ChargeSummary; import com.lanternsoftware.datamodel.currentmonitor.ChargeTotal; import com.lanternsoftware.datamodel.currentmonitor.EnergySummary; @@ -16,7 +17,12 @@ import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode; import com.lanternsoftware.datamodel.currentmonitor.HubCommand; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.Sequence; +import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus; +import com.lanternsoftware.datamodel.currentmonitor.archive.BreakerEnergyArchive; +import com.lanternsoftware.datamodel.currentmonitor.archive.DailyEnergyArchive; +import com.lanternsoftware.datamodel.currentmonitor.archive.MonthlyEnergyArchive; import com.lanternsoftware.util.CollectionUtils; +import com.lanternsoftware.util.DateRange; import com.lanternsoftware.util.DateUtils; import com.lanternsoftware.util.DebugTimer; import com.lanternsoftware.util.NullUtils; @@ -28,24 +34,41 @@ import com.lanternsoftware.util.dao.DaoSort; import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.mongo.MongoConfig; import com.lanternsoftware.util.dao.mongo.MongoProxy; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.mutable.MutableDouble; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.mindrot.jbcrypt.BCrypt; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; +import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.TimeZone; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.zip.Deflater; +import java.util.zip.GZIPOutputStream; public class MongoCurrentMonitorDao implements CurrentMonitorDao { private static final Logger logger = LoggerFactory.getLogger(MongoCurrentMonitorDao.class); @@ -65,6 +88,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { proxy.ensureIndex(ChargeSummary.class, DaoSort.sort("account_id").then("plan_id").then("group_id").then("view_mode")); proxy.ensureIndex(ChargeTotal.class, DaoSort.sort("account_id").then("plan_id").then("group_id").then("view_mode").then("start")); proxy.ensureIndex(DirtyMinute.class, DaoSort.sort("posted")); + proxy.ensureIndex(ArchiveStatus.class, DaoSort.sort("account_id")); for (DirtyMinute minute : proxy.queryAll(DirtyMinute.class)) { updateEnergySummaries(minute); } @@ -100,6 +124,196 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { }, 10000); } + @Override + public Iterable streamHubPowerMinutes(int _accountId, Date _start, Date _end) { + return proxy.queryIterator(HubPowerMinute.class, new DaoQuery("account_id", _accountId).andBetweenInclusiveExclusive("minute", DateUtils.toLong(_start)/60000, DateUtils.toLong(_end)/60000), null, DaoSort.sort("start"), 0, 0); + } + + @Override + public void archiveMonth(int _accountId, Date _month) { + ArchiveStatus status = new ArchiveStatus(); + status.setAccountId(_accountId); + status.setMonth(_month); + status.setProgress(1); + putArchiveStatus(status); + executor.submit(()->{ + synchronized (MongoCurrentMonitorDao.this) { + TimeZone tz = getTimeZoneForAccount(_accountId); + DebugTimer timer = new DebugTimer("Monthly Archive Generation for account " + _accountId + " month " + DateUtils.format("MMMM yyyy", tz, _month)); + Date start = _month; + Date end = DateUtils.getEndOfMonth(_month, tz); + BreakerConfig config = getConfig(_accountId); //TODO: get historical config for archive month in case it's changed since then. + List breakers = CollectionUtils.filter(config.getAllBreakers(), _b -> !NullUtils.isOneOf(_b.getType(), BreakerType.DOUBLE_POLE_BOTTOM, BreakerType.EMPTY)); + breakers.sort(Comparator.comparing(Breaker::getPanel).thenComparing(Breaker::getSpace)); + Map breakerKeys = CollectionUtils.transformToMap(config.getAllBreakers(), Breaker::getIntKey, _b -> Breaker.intKey(_b.getPanel(), _b.getType() == BreakerType.DOUBLE_POLE_BOTTOM ? _b.getSpace() - 2 : _b.getSpace())); + Map> minuteReadings = new HashMap<>(); + MonthlyEnergyArchive archive = new MonthlyEnergyArchive(); + archive.setAccountId(_accountId); + archive.setMonth(start); + List days = new ArrayList<>(); + archive.setDays(days); + while (start.before(end)) { + Map dayReadings = new HashMap<>(); + Date dayEnd = DateUtils.addDays(start, 1, tz); + int minute = 0; + int bytesInDay = (int) (4 * DateUtils.diffInSeconds(start, dayEnd)); + Iterator i = streamHubPowerMinutes(_accountId, start, dayEnd).iterator(); + HubPowerMinute m = null; + if (i.hasNext()) + m = i.next(); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + while (i.hasNext()) { + if (m == null) + break; + for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(m.getBreakers())) { + if (!breakerKeys.containsKey(breaker.breakerIntKey())) + continue; + int key = breakerKeys.get(breaker.breakerIntKey()); + List r = minuteReadings.get(key); + if (r == null) + minuteReadings.put(key, breaker.getReadings()); + else { + for (int idx = 0; idx < minuteReadings.size(); idx++) { + r.set(idx, r.get(idx) + breaker.getReadings().get(idx)); + } + } + } + HubPowerMinute cur = i.next(); + if (cur.getMinute() != m.getMinute()) { + addReadings(minute, bytesInDay, minuteReadings, dayReadings); + minute++; + } + m = cur; + } + if (m != null) + addReadings(minute, bytesInDay, minuteReadings, dayReadings); + List breakerEnergies = new ArrayList<>(); + for (Entry be : dayReadings.entrySet()) { + BreakerEnergyArchive breakerEnergy = new BreakerEnergyArchive(); + breakerEnergy.setPanel(Breaker.intKeyToPanel(be.getKey())); + breakerEnergy.setSpace(Breaker.intKeyToSpace(be.getKey())); + breakerEnergy.setReadings(be.getValue()); + breakerEnergies.add(breakerEnergy); + } + DailyEnergyArchive day = new DailyEnergyArchive(); + day.setBreakers(breakerEnergies); + days.add(day); + start = dayEnd; + status.setProgress(50f * (start.getTime() - _month.getTime()) / (end.getTime() - _month.getTime())); + putArchiveStatus(status); + } + timer.stop(); + DebugTimer t = new DebugTimer("Convert Archive to bson for account " + archive.getAccountId()); + byte[] bson = DaoSerializer.toBson(archive); + t.stop(); + + DebugTimer t2 = new DebugTimer("Zip Archive and write to disk for account" + archive.getAccountId()); + OutputStream os = null; + try { + File partialPath = new File(LanternFiles.BACKUP_DEST_PATH + archive.getAccountId()+File.separator + "partial"); + FileUtils.deleteDirectory(partialPath); + partialPath.mkdirs(); + String backupPath = LanternFiles.BACKUP_DEST_PATH + archive.getAccountId() + File.separator; + if (!archive.isComplete(tz)) + backupPath += "partial" + File.separator; + os = new GZIPOutputStream(new FileOutputStream(backupPath + archive.getMonth().getTime() + ".zip")) {{def.setLevel(Deflater.BEST_SPEED);}}; + int batchSize = bson.length / 50; + for (int offset = 0; offset < bson.length; offset += batchSize) { + os.write(bson, offset, Math.min(batchSize, bson.length - offset)); + status.setProgress(50 + (50f * offset / bson.length)); + putArchiveStatus(status); + } + } catch (Exception _e) { + logger.error("Failed to write export file", _e); + } finally { + IOUtils.closeQuietly(os); + } + t2.stop(); + deleteArchiveStatus(_accountId, _month); + } + }); + } + + private void addReadings(int _minuteInDay, int _bytesInDay, Map> _minuteReadings, Map _dayReadings) { + for (Entry> entry : _minuteReadings.entrySet()) { + byte[] dayBytes = _dayReadings.computeIfAbsent(entry.getKey(), _r->new byte[_bytesInDay]); + ByteBuffer bb = ByteBuffer.wrap(dayBytes); + for (int fl = 0; fl < CollectionUtils.size(entry.getValue()); fl++) { + bb.putFloat(_minuteInDay*240 + (fl*4), CollectionUtils.get(entry.getValue(), fl)); + } + } + _minuteReadings.clear(); + } + + @Override + public InputStream streamArchive(int _accountId, Date _month) { + try { + String complete = LanternFiles.BACKUP_DEST_PATH + _accountId + File.separator + _month.getTime() + ".zip"; + if (new File(complete).exists()) + return new FileInputStream(complete); + String partial = LanternFiles.BACKUP_DEST_PATH + _accountId + File.separator + "partial" + File.separator + _month.getTime() + ".zip"; + if (new File(partial).exists()) + return new FileInputStream(partial); + } + catch (Exception _e) { + logger.error("Failed to load archive", _e); + } + return null; + } + + @Override + public void putArchiveStatus(ArchiveStatus _status) { + proxy.save(_status); + } + + @Override + public void deleteArchiveStatus(int _accountId, Date _month) { + proxy.delete(ArchiveStatus.class, new DaoQuery("_id", MonthlyEnergyArchive.toId(_accountId, _month))); + } + + @Override + public List getArchiveStatus(int _accountId) { + Map statuses = CollectionUtils.transformToSortedMap(proxy.query(ArchiveStatus.class, new DaoQuery("account_id", _accountId)), ArchiveStatus::getMonth); + File folder = new File(LanternFiles.BACKUP_DEST_PATH + _accountId); + if (folder.exists()) { + for (File file : CollectionUtils.asArrayList(folder.listFiles())) { + if (file.isFile()) { + Date month = new Date(DaoSerializer.toLong(file.getName().replace(".zip", ""))); + statuses.computeIfAbsent(month, _m -> new ArchiveStatus(_accountId, _m, 100)); + } + } + } + File partial = new File(LanternFiles.BACKUP_DEST_PATH + _accountId + File.separator + "partial"); + if (partial.exists()) { + for (File file : CollectionUtils.asArrayList(partial.listFiles())) { + if (file.isFile() && (new Date().getTime() - file.lastModified() < 86400000)) { + Date month = new Date(DaoSerializer.toLong(file.getName().replace(".zip", ""))); + statuses.computeIfAbsent(month, _m -> new ArchiveStatus(_accountId, _m, 100)); + } + } + } + DateRange range = getMonitoredDateRange(_accountId); + TimeZone tz = getTimeZoneForAccount(_accountId); + Date month = DateUtils.getStartOfMonth(range.getStart(), tz); + Date end = DateUtils.getEndOfMonth(range.getEnd(), tz); + while (month.before(end)) { + statuses.computeIfAbsent(month, _m->new ArchiveStatus(_accountId, _m, 0)); + month = DateUtils.addMonths(month, 1, tz); + } + return new ArrayList<>(statuses.values()); + } + + @Override + public DateRange getMonitoredDateRange(int _accountId) { + DaoQuery query = new DaoQuery("account_id", _accountId).and("view_mode", EnergyViewMode.MONTH.name()); + EnergySummary first = proxy.queryOne(EnergySummary.class, query, DaoSort.sort("start")); + EnergySummary last = proxy.queryOne(EnergySummary.class, query, DaoSort.sortDesc("start")); + if ((first != null) && (last != null)) + return new DateRange(first.getStart(), last.getStart()); + return null; + } + private void updateEnergySummaries(DirtyMinute _minute) { DebugTimer timer = new DebugTimer("Updating summaries", logger); List minutes = proxy.query(HubPowerMinute.class, new DaoQuery("account_id", _minute.getAccountId()).and("minute", _minute.getMinute())); @@ -262,32 +476,6 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { } } - private void rebuildSummaries(int _accountId, Collection _rates) { - logger.info("Rebuilding summaries due to a change in {} rates", CollectionUtils.size(_rates)); - HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute")); - if (firstMinute == null) - return; - TimeZone tz = getTimeZoneForAccount(_accountId); - Map rates = CollectionUtils.transformToMap(_rates, _r -> String.format("%d%d", DaoSerializer.toLong(_r.getBeginEffective()), DaoSerializer.toLong(_r.getEndEffective()))); - for (BillingRate rate : rates.values()) { - Date start = rate.getBeginEffective(); - Date end = rate.getEndEffective(); - Date now = new Date(); - if ((start == null) || start.before(firstMinute.getMinuteAsDate())) - start = firstMinute.getMinuteAsDate(); - if ((end == null) || end.after(now)) - end = now; - rebuildSummaries(_accountId, start, end); - if (rate.isRecursAnnually()) { - while (end.before(now)) { - start = DateUtils.addYears(start, 1, tz); - end = DateUtils.addYears(end, 1, tz); - rebuildSummaries(_accountId, start, end); - } - } - } - } - @Override public void rebuildSummariesAsync(int _accountId) { executor.submit(() -> rebuildSummaries(_accountId)); @@ -488,13 +676,13 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { public String getAuthCodeForEmail(String _email, TimeZone _tz) { _email = _email.toLowerCase().trim(); Account account = getAccountByUsername(_email); - if (account == null) { + if ((account == null) && (_tz != null)) { account = new Account(); account.setUsername(_email); account.setTimezone(_tz.getID()); putAccount(account); } - return toAuthCode(account.getId(), account.getAuxiliaryAccountIds()); + return (account == null)?null:toAuthCode(account.getId(), account.getAuxiliaryAccountIds()); } public String toAuthCode(int _acctId, List _auxAcctIds) { diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Breaker.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Breaker.java index 2001613..8294e17 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Breaker.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Breaker.java @@ -194,10 +194,26 @@ public class Breaker implements IIdentical { return key; } + public int getIntKey() { + return intKey(panel, space); + } + + public static int intKeyToPanel(int _intKey) { + return _intKey/10000; + } + + public static int intKeyToSpace(int _intKey) { + return _intKey%10000; + } + public static String key(int _panel, int _space) { return String.format("%d-%d", _panel, _space); } + public static int intKey(int _panel, int _space) { + return 10000*_panel + _space; + } + public static int portToChip(int _port) { return (_port < 9) ? 1 : 0; } diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerPowerMinute.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerPowerMinute.java index a995e2b..c529fd3 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerPowerMinute.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerPowerMinute.java @@ -30,6 +30,10 @@ public class BreakerPowerMinute { return Breaker.key(panel, space); } + public int breakerIntKey() { + return Breaker.intKey(panel, space); + } + public List getReadings() { return readings; } diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/ArchiveStatus.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/ArchiveStatus.java new file mode 100644 index 0000000..b854ea2 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/ArchiveStatus.java @@ -0,0 +1,50 @@ +package com.lanternsoftware.datamodel.currentmonitor.archive; + +import com.lanternsoftware.util.DateUtils; +import com.lanternsoftware.util.dao.annotations.DBSerializable; + +import java.util.Date; + +@DBSerializable(autogen = false) +public class ArchiveStatus { + private int accountId; + private Date month; + private float progress; + + public ArchiveStatus() { + } + + public ArchiveStatus(int _accountId, Date _month, float _progress) { + accountId = _accountId; + month = _month; + progress = _progress; + } + + public String getId() { + return String.format("%d-%d", accountId, DateUtils.toLong(month)); + } + + public int getAccountId() { + return accountId; + } + + public void setAccountId(int _accountId) { + accountId = _accountId; + } + + public Date getMonth() { + return month; + } + + public void setMonth(Date _month) { + month = _month; + } + + public float getProgress() { + return progress; + } + + public void setProgress(float _progress) { + progress = _progress; + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/BreakerEnergyArchive.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/BreakerEnergyArchive.java new file mode 100644 index 0000000..f3d4835 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/BreakerEnergyArchive.java @@ -0,0 +1,34 @@ +package com.lanternsoftware.datamodel.currentmonitor.archive; + +import com.lanternsoftware.util.dao.annotations.DBSerializable; + +@DBSerializable +public class BreakerEnergyArchive { + private int panel; + private int space; + private byte[] readings; + + public int getPanel() { + return panel; + } + + public void setPanel(int _panel) { + panel = _panel; + } + + public int getSpace() { + return space; + } + + public void setSpace(int _space) { + space = _space; + } + + public byte[] getReadings() { + return readings; + } + + public void setReadings(byte[] _readings) { + readings = _readings; + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/DailyEnergyArchive.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/DailyEnergyArchive.java new file mode 100644 index 0000000..23198d4 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/DailyEnergyArchive.java @@ -0,0 +1,18 @@ +package com.lanternsoftware.datamodel.currentmonitor.archive; + +import com.lanternsoftware.util.dao.annotations.DBSerializable; + +import java.util.List; + +@DBSerializable +public class DailyEnergyArchive { + private List breakers; + + public List getBreakers() { + return breakers; + } + + public void setBreakers(List _breakers) { + breakers = _breakers; + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/MonthlyEnergyArchive.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/MonthlyEnergyArchive.java new file mode 100644 index 0000000..172434c --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/MonthlyEnergyArchive.java @@ -0,0 +1,53 @@ +package com.lanternsoftware.datamodel.currentmonitor.archive; + +import com.lanternsoftware.util.DateUtils; +import com.lanternsoftware.util.dao.annotations.DBSerializable; + +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +@DBSerializable(autogen = false) +public class MonthlyEnergyArchive { + private int accountId; + private Date month; + private List days; + + public String getId() { + return toId(accountId, month); + } + + public static String toId(int _accountId, Date _month) { + return String.format("%d-%d", _accountId, DateUtils.toLong(_month)); + } + + public int getAccountId() { + return accountId; + } + + public void setAccountId(int _accountId) { + accountId = _accountId; + } + + public Date getMonth() { + return month; + } + + public void setMonth(Date _month) { + month = _month; + } + + public List getDays() { + return days; + } + + public void setDays(List _days) { + days = _days; + } + + public boolean isComplete(TimeZone _tz) { + Date valid = DateUtils.addDays(new Date(), -7, _tz); + valid = DateUtils.addMonths(valid, -1, _tz); + return month.before(valid); + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/ArchiveStatusSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/ArchiveStatusSerializer.java new file mode 100644 index 0000000..e1567f0 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/ArchiveStatusSerializer.java @@ -0,0 +1,44 @@ +package com.lanternsoftware.datamodel.currentmonitor.archive.dao; + +import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus; +import com.lanternsoftware.util.dao.AbstractDaoSerializer; +import com.lanternsoftware.util.dao.DaoEntity; +import com.lanternsoftware.util.dao.DaoProxyType; +import com.lanternsoftware.util.dao.DaoSerializer; +import java.util.Collections; +import java.util.List; + +public class ArchiveStatusSerializer extends AbstractDaoSerializer +{ + @Override + public Class getSupportedClass() + { + return ArchiveStatus.class; + } + + @Override + public List getSupportedProxies() { + return Collections.singletonList(DaoProxyType.MONGO); + } + + @Override + public DaoEntity toDaoEntity(ArchiveStatus _o) + { + DaoEntity d = new DaoEntity(); + d.put("_id", _o.getId()); + d.put("account_id", _o.getAccountId()); + d.put("month", DaoSerializer.toLong(_o.getMonth())); + d.put("progress", _o.getProgress()); + return d; + } + + @Override + public ArchiveStatus fromDaoEntity(DaoEntity _d) + { + ArchiveStatus o = new ArchiveStatus(); + o.setAccountId(DaoSerializer.getInteger(_d, "account_id")); + o.setMonth(DaoSerializer.getDate(_d, "month")); + o.setProgress(DaoSerializer.getFloat(_d, "progress")); + return o; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/BreakerEnergyArchiveSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/BreakerEnergyArchiveSerializer.java new file mode 100644 index 0000000..269b374 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/BreakerEnergyArchiveSerializer.java @@ -0,0 +1,43 @@ +package com.lanternsoftware.datamodel.currentmonitor.archive.dao; + +import com.lanternsoftware.datamodel.currentmonitor.archive.BreakerEnergyArchive; +import com.lanternsoftware.util.dao.AbstractDaoSerializer; +import com.lanternsoftware.util.dao.DaoEntity; +import com.lanternsoftware.util.dao.DaoProxyType; +import com.lanternsoftware.util.dao.DaoSerializer; +import java.util.Collections; +import java.util.List; + +public class BreakerEnergyArchiveSerializer extends AbstractDaoSerializer +{ + @Override + public Class getSupportedClass() + { + return BreakerEnergyArchive.class; + } + + @Override + public List getSupportedProxies() { + return Collections.singletonList(DaoProxyType.MONGO); + } + + @Override + public DaoEntity toDaoEntity(BreakerEnergyArchive _o) + { + DaoEntity d = new DaoEntity(); + d.put("panel", _o.getPanel()); + d.put("space", _o.getSpace()); + d.put("readings", _o.getReadings()); + return d; + } + + @Override + public BreakerEnergyArchive fromDaoEntity(DaoEntity _d) + { + BreakerEnergyArchive o = new BreakerEnergyArchive(); + o.setPanel(DaoSerializer.getInteger(_d, "panel")); + o.setSpace(DaoSerializer.getInteger(_d, "space")); + o.setReadings(DaoSerializer.getByteArray(_d, "readings")); + return o; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/DailyEnergyArchiveSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/DailyEnergyArchiveSerializer.java new file mode 100644 index 0000000..e5c34e8 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/DailyEnergyArchiveSerializer.java @@ -0,0 +1,40 @@ +package com.lanternsoftware.datamodel.currentmonitor.archive.dao; + +import com.lanternsoftware.datamodel.currentmonitor.archive.BreakerEnergyArchive; +import com.lanternsoftware.datamodel.currentmonitor.archive.DailyEnergyArchive; +import com.lanternsoftware.util.dao.AbstractDaoSerializer; +import com.lanternsoftware.util.dao.DaoEntity; +import com.lanternsoftware.util.dao.DaoProxyType; +import com.lanternsoftware.util.dao.DaoSerializer; +import java.util.Collections; +import java.util.List; + +public class DailyEnergyArchiveSerializer extends AbstractDaoSerializer +{ + @Override + public Class getSupportedClass() + { + return DailyEnergyArchive.class; + } + + @Override + public List getSupportedProxies() { + return Collections.singletonList(DaoProxyType.MONGO); + } + + @Override + public DaoEntity toDaoEntity(DailyEnergyArchive _o) + { + DaoEntity d = new DaoEntity(); + d.put("breakers", DaoSerializer.toDaoEntities(_o.getBreakers(), DaoProxyType.MONGO)); + return d; + } + + @Override + public DailyEnergyArchive fromDaoEntity(DaoEntity _d) + { + DailyEnergyArchive o = new DailyEnergyArchive(); + o.setBreakers(DaoSerializer.getList(_d, "breakers", BreakerEnergyArchive.class)); + return o; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/MonthlyEnergyArchiveSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/MonthlyEnergyArchiveSerializer.java new file mode 100644 index 0000000..c484af2 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/archive/dao/MonthlyEnergyArchiveSerializer.java @@ -0,0 +1,45 @@ +package com.lanternsoftware.datamodel.currentmonitor.archive.dao; + +import com.lanternsoftware.datamodel.currentmonitor.archive.DailyEnergyArchive; +import com.lanternsoftware.datamodel.currentmonitor.archive.MonthlyEnergyArchive; +import com.lanternsoftware.util.dao.AbstractDaoSerializer; +import com.lanternsoftware.util.dao.DaoEntity; +import com.lanternsoftware.util.dao.DaoProxyType; +import com.lanternsoftware.util.dao.DaoSerializer; +import java.util.Collections; +import java.util.List; + +public class MonthlyEnergyArchiveSerializer extends AbstractDaoSerializer +{ + @Override + public Class getSupportedClass() + { + return MonthlyEnergyArchive.class; + } + + @Override + public List getSupportedProxies() { + return Collections.singletonList(DaoProxyType.MONGO); + } + + @Override + public DaoEntity toDaoEntity(MonthlyEnergyArchive _o) + { + DaoEntity d = new DaoEntity(); + d.put("_id", _o.getId()); + d.put("account_id", _o.getAccountId()); + d.put("month", DaoSerializer.toLong(_o.getMonth())); + d.put("days", DaoSerializer.toDaoEntities(_o.getDays(), DaoProxyType.MONGO)); + return d; + } + + @Override + public MonthlyEnergyArchive fromDaoEntity(DaoEntity _d) + { + MonthlyEnergyArchive o = new MonthlyEnergyArchive(); + o.setAccountId(DaoSerializer.getInteger(_d, "account_id")); + o.setMonth(DaoSerializer.getDate(_d, "month")); + o.setDays(DaoSerializer.getList(_d, "days", DailyEnergyArchive.class)); + return o; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer b/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer index 3a3f460..c3d0dd6 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer @@ -1,3 +1,7 @@ +com.lanternsoftware.datamodel.currentmonitor.archive.dao.ArchiveStatusSerializer +com.lanternsoftware.datamodel.currentmonitor.archive.dao.BreakerEnergyArchiveSerializer +com.lanternsoftware.datamodel.currentmonitor.archive.dao.DailyEnergyArchiveSerializer +com.lanternsoftware.datamodel.currentmonitor.archive.dao.MonthlyEnergyArchiveSerializer com.lanternsoftware.datamodel.currentmonitor.dao.AccountSerializer com.lanternsoftware.datamodel.currentmonitor.dao.BillingPlanSerializer com.lanternsoftware.datamodel.currentmonitor.dao.BillingRateSerializer diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/context/Globals.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/context/Globals.java index e4f387c..e2d041e 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/context/Globals.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/context/Globals.java @@ -6,7 +6,7 @@ import com.lanternsoftware.datamodel.currentmonitor.HubCommand; import com.lanternsoftware.datamodel.currentmonitor.HubCommands; import com.lanternsoftware.rules.RulesEngine; import com.lanternsoftware.util.DateUtils; -import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.dao.mongo.MongoConfig; import javax.servlet.ServletContextEvent; @@ -24,7 +24,7 @@ public class Globals implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { - dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg")); + dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg")); RulesEngine.instance().start(); RulesEngine.instance().schedule(new CommandTask(), 0); } diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/AuthServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/AuthServlet.java index ccab743..c943482 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/AuthServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/AuthServlet.java @@ -1,17 +1,10 @@ package com.lanternsoftware.currentmonitor.servlet; -import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest; -import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; -import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; -import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.api.client.json.gson.GsonFactory; import com.lanternsoftware.currentmonitor.context.Globals; +import com.lanternsoftware.currentmonitor.util.GoogleAuthHelper; import com.lanternsoftware.util.DateUtils; -import com.lanternsoftware.util.LanternFiles; import com.lanternsoftware.util.NullUtils; -import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.dao.DaoEntity; -import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.servlet.BasicAuth; import com.lanternsoftware.util.servlet.LanternServlet; import org.slf4j.Logger; @@ -23,15 +16,7 @@ import javax.servlet.http.HttpServletResponse; @WebServlet("/auth/*") public class AuthServlet extends LanternServlet { - private static final NetHttpTransport transport = new NetHttpTransport(); private static final Logger logger = LoggerFactory.getLogger(AuthServlet.class); - private static final String googleClientId; - private static final String googleClientSecret; - static { - DaoEntity google = DaoSerializer.parse(ResourceLoader.loadFileAsString(LanternFiles.OPS_PATH + "google_sso.txt")); - googleClientId = DaoSerializer.getString(google, "id"); - googleClientSecret = DaoSerializer.getString(google, "secret"); - } @Override protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { @@ -40,16 +25,7 @@ public class AuthServlet extends LanternServlet { BasicAuth auth = new BasicAuth(_req); if (NullUtils.isEqual(auth.getUsername(), "googlesso")) { logger.info("Attempting google SSO"); - try { - GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(transport, new GsonFactory(), "https://oauth2.googleapis.com/token", googleClientId, googleClientSecret, auth.getPassword(), "").execute(); - if (tokenResponse != null) { - GoogleIdToken idToken = tokenResponse.parseIdToken(); - if (idToken != null) - authCode = Globals.dao.getAuthCodeForEmail(idToken.getPayload().getEmail(), DateUtils.fromTimeZoneId(_req.getHeader("timezone"))); - } - } catch (Exception _e) { - logger.error("Failed to validate google auth code", _e); - } + authCode = GoogleAuthHelper.signin(auth.getPassword(), DateUtils.fromTimeZoneId(_req.getHeader("timezone"))); } else authCode = Globals.dao.authenticateAccount(auth.getUsername(), auth.getPassword()); } diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ChargeServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ChargeServlet.java index 4effc31..ec9832b 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ChargeServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ChargeServlet.java @@ -14,7 +14,7 @@ import javax.ws.rs.core.MediaType; import java.util.Date; @WebServlet("/charge/*") -public class ChargeServlet extends SecureServlet { +public class ChargeServlet extends SecureServiceServlet { @Override protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { String[] path = path(_req); diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/CommandServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/CommandServlet.java index a58aad0..2097337 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/CommandServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/CommandServlet.java @@ -7,7 +7,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/command") -public class CommandServlet extends SecureServlet { +public class CommandServlet extends SecureServiceServlet { @Override protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ConfigServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ConfigServlet.java index d746628..b6c7702 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ConfigServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ConfigServlet.java @@ -13,7 +13,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/config/*") -public class ConfigServlet extends SecureServlet { +public class ConfigServlet extends SecureServiceServlet { private static final Logger logger = LoggerFactory.getLogger(ConfigServlet.class); @Override diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/EnergyServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/EnergyServlet.java index 1652317..4f1d556 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/EnergyServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/EnergyServlet.java @@ -14,7 +14,7 @@ import javax.ws.rs.core.MediaType; import java.util.Date; @WebServlet("/energy/*") -public class EnergyServlet extends SecureServlet { +public class EnergyServlet extends SecureServiceServlet { @Override protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { String[] path = path(_req); diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/FreemarkerCMServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/FreemarkerCMServlet.java new file mode 100644 index 0000000..f5f9304 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/FreemarkerCMServlet.java @@ -0,0 +1,23 @@ +package com.lanternsoftware.currentmonitor.servlet; + +import com.lanternsoftware.util.servlet.FreemarkerConfigUtil; +import com.lanternsoftware.util.servlet.FreemarkerServlet; +import com.lanternsoftware.util.servlet.FreemarkerUtil; +import freemarker.template.Configuration; + +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +public abstract class FreemarkerCMServlet extends FreemarkerServlet { + protected static final Configuration CONFIG = FreemarkerConfigUtil.createConfig(FreemarkerCMServlet.class, "/templates", 100); + + @Override + protected Configuration getFreemarkerConfig() { + return CONFIG; + } + + public void renderBody(HttpServletResponse _rep, String _sHtmlResourceKey, Map _mapModel) { + _mapModel.put("body", FreemarkerUtil.render(getFreemarkerConfig(), _sHtmlResourceKey, _mapModel)); + render(_rep, "frame.ftl", _mapModel); + } +} diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GenerateBomServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GenerateBomServlet.java index 52c0893..6578c64 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GenerateBomServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GenerateBomServlet.java @@ -9,7 +9,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/generateBom") -public class GenerateBomServlet extends SecureServlet { +public class GenerateBomServlet extends SecureServiceServlet { @Override protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { AuthCode authCode = Globals.dao.decryptAuthCode(_req.getHeader("auth_code")); diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GroupEnergyServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GroupEnergyServlet.java index 7db3044..031da97 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GroupEnergyServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GroupEnergyServlet.java @@ -15,7 +15,7 @@ import javax.ws.rs.core.MediaType; import java.util.*; @WebServlet("/energy/group/*") -public class GroupEnergyServlet extends SecureServlet { +public class GroupEnergyServlet extends SecureServiceServlet { @Override protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { String[] path = path(_req); diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GroupPowerServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GroupPowerServlet.java index 29b4ed8..c5326c8 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GroupPowerServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/GroupPowerServlet.java @@ -10,7 +10,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/power/group/*") -public class GroupPowerServlet extends SecureServlet { +public class GroupPowerServlet extends SecureServiceServlet { @Override protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { String[] path = path(_req); diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/PowerServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/PowerServlet.java index f159626..3864de8 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/PowerServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/PowerServlet.java @@ -19,7 +19,7 @@ import javax.servlet.http.HttpServletResponse; import java.util.List; @WebServlet("/power/*") -public class PowerServlet extends SecureServlet { +public class PowerServlet extends SecureServiceServlet { private static final Logger logger = LoggerFactory.getLogger(MongoCurrentMonitorDao.class); @Override diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/RebuildSummariesServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/RebuildSummariesServlet.java index 2719412..9befc30 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/RebuildSummariesServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/RebuildSummariesServlet.java @@ -3,7 +3,6 @@ package com.lanternsoftware.currentmonitor.servlet; import com.lanternsoftware.currentmonitor.context.Globals; import com.lanternsoftware.datamodel.currentmonitor.Account; import com.lanternsoftware.util.CollectionUtils; -import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.auth.AuthCode; @@ -12,7 +11,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/rebuildSummaries/*") -public class RebuildSummariesServlet extends SecureServlet { +public class RebuildSummariesServlet extends SecureServiceServlet { @Override protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { if (_authCode.getAccountId() == 100) { diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ResetPasswordServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ResetPasswordServlet.java index 32f521b..6004e0c 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ResetPasswordServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ResetPasswordServlet.java @@ -1,7 +1,8 @@ package com.lanternsoftware.currentmonitor.servlet; import com.lanternsoftware.currentmonitor.context.Globals; -import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.CollectionUtils; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.dao.DaoEntity; @@ -30,7 +31,7 @@ import java.io.IOException; public class ResetPasswordServlet extends FreemarkerServlet { protected static final Logger LOG = LoggerFactory.getLogger(ResetPasswordServlet.class); protected static final Configuration CONFIG = FreemarkerConfigUtil.createConfig(ResetPasswordServlet.class, "/templates", 100); - protected static final String api_key = ResourceLoader.loadFileAsString(LanternFiles.OPS_PATH + "sendgrid.txt"); + protected static final String api_key = ResourceLoader.loadFileAsString(LanternFiles.CONFIG_PATH + "sendgrid.txt"); @Override protected Configuration getFreemarkerConfig() { @@ -40,7 +41,7 @@ public class ResetPasswordServlet extends FreemarkerServlet { @Override protected void doGet(HttpServletRequest _req, HttpServletResponse _resp) { String[] path = getPath(_req); - String email = Globals.dao.getEmailForResetKey(path[1]); + String email = Globals.dao.getEmailForResetKey(CollectionUtils.get(path, 1)); if (EmailValidator.getInstance().isValid(email)) { render(_resp, "passwordReset.ftl", model(_req, "key", path[1])); } else { diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/SecureServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/SecureServiceServlet.java similarity index 93% rename from currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/SecureServlet.java rename to currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/SecureServiceServlet.java index 8dec803..5ad5340 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/SecureServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/SecureServiceServlet.java @@ -7,7 +7,7 @@ import com.lanternsoftware.util.servlet.LanternServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public abstract class SecureServlet extends LanternServlet { +public abstract class SecureServiceServlet extends LanternServlet { @Override protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { AuthCode authCode = Globals.dao.decryptAuthCode(_req.getHeader("auth_code")); diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/UpdateServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/UpdateServlet.java index 6383c7b..24b945f 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/UpdateServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/UpdateServlet.java @@ -1,6 +1,6 @@ package com.lanternsoftware.currentmonitor.servlet; -import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.servlet.LanternServlet; @@ -16,8 +16,8 @@ public class UpdateServlet extends LanternServlet { @Override protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { if (isPath(_req, 0, "version")) - setResponseEntity(_rep, MediaType.APPLICATION_OCTET_STREAM, DaoSerializer.toZipBson(DaoSerializer.parse(ResourceLoader.loadFileAsString(LanternFiles.OPS_PATH + "release" + File.separator + "version.json")))); + setResponseEntity(_rep, MediaType.APPLICATION_OCTET_STREAM, DaoSerializer.toZipBson(DaoSerializer.parse(ResourceLoader.loadFileAsString(LanternFiles.CONFIG_PATH + "release" + File.separator + "version.json")))); else - setResponseEntity(_rep, MediaType.APPLICATION_OCTET_STREAM, ResourceLoader.loadFile(LanternFiles.OPS_PATH + "release" + File.separator + "lantern-currentmonitor.jar")); + setResponseEntity(_rep, MediaType.APPLICATION_OCTET_STREAM, ResourceLoader.loadFile(LanternFiles.CONFIG_PATH + "release" + File.separator + "lantern-currentmonitor.jar")); } } diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ConsoleServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ConsoleServlet.java new file mode 100644 index 0000000..6ccdbe1 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ConsoleServlet.java @@ -0,0 +1,19 @@ +package com.lanternsoftware.currentmonitor.servlet.console; + +import com.lanternsoftware.util.dao.auth.AuthCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet("") +public class ConsoleServlet extends SecureConsoleServlet { + private static final Logger logger = LoggerFactory.getLogger(ConsoleServlet.class); + + @Override + protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { + redirect(_rep, "export"); + } +} diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ExportServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ExportServlet.java new file mode 100644 index 0000000..deec364 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ExportServlet.java @@ -0,0 +1,140 @@ +package com.lanternsoftware.currentmonitor.servlet.console; + +import com.lanternsoftware.currentmonitor.context.Globals; +import com.lanternsoftware.datamodel.currentmonitor.Breaker; +import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig; +import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus; +import com.lanternsoftware.datamodel.currentmonitor.archive.BreakerEnergyArchive; +import com.lanternsoftware.datamodel.currentmonitor.archive.DailyEnergyArchive; +import com.lanternsoftware.datamodel.currentmonitor.archive.MonthlyEnergyArchive; +import com.lanternsoftware.util.CollectionUtils; +import com.lanternsoftware.util.DateUtils; +import com.lanternsoftware.util.NullUtils; +import com.lanternsoftware.util.dao.DaoEntity; +import com.lanternsoftware.util.dao.DaoSerializer; +import com.lanternsoftware.util.dao.auth.AuthCode; +import org.apache.commons.io.IOUtils; +import org.bson.codecs.DocumentCodec; +import org.bson.codecs.EncoderContext; +import org.bson.json.JsonWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; +import java.util.zip.Deflater; +import java.util.zip.GZIPOutputStream; + +@WebServlet("/export/*") +public class ExportServlet extends SecureConsoleServlet { + private static final Logger logger = LoggerFactory.getLogger(ExportServlet.class); + + @Override + protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { + TimeZone tz = Globals.dao.getTimeZoneForAccount(_authCode.getAccountId()); + String[] path = path(_req); + if (path.length > 1) { + synchronized (this) { + InputStream is = Globals.dao.streamArchive(_authCode.getAccountId(), new Date(DaoSerializer.toLong(path[0]))); + if (is == null) { + redirect(_rep, _req.getContextPath() + "/export"); + return; + } + OutputStream os = null; + GZIPOutputStream gout = null; + JsonWriter jsonWriter = null; + try { + os = _rep.getOutputStream(); + if (NullUtils.makeNotNull(path[1]).contains("csv")) { + BreakerConfig config = Globals.dao.getConfig(_authCode.getAccountId()); //TODO: get historical config for this month in case it's changed since then. + Map breakers = CollectionUtils.transformToMap(config.getAllBreakers(), Breaker::getIntKey); + os = new GZIPOutputStream(os) {{def.setLevel(Deflater.BEST_SPEED);}}; + MonthlyEnergyArchive archive = DaoSerializer.fromZipBson(IOUtils.toByteArray(is), MonthlyEnergyArchive.class); + DailyEnergyArchive fday = CollectionUtils.getFirst(archive.getDays()); + if (fday == null) { + redirect(_rep, _req.getContextPath() + "/export"); + return; + } + StringBuilder header = new StringBuilder("Timestamp"); + for (BreakerEnergyArchive ba : CollectionUtils.makeNotNull(fday.getBreakers())) { + Breaker b = breakers.get(Breaker.intKey(ba.getPanel(), ba.getSpace())); + header.append(","); + if (b != null) { + header.append(b.getKey()); + header.append("-"); + header.append(b.getName()); + } + } + header.append("\n"); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + os.write(NullUtils.toByteArray(header.toString())); + Date dayStart = archive.getMonth(); + for (DailyEnergyArchive day : CollectionUtils.makeNotNull(archive.getDays())) { + Date dayEnd = DateUtils.addDays(dayStart, 1, tz); + int secondsInDay = (int) ((dayEnd.getTime() - dayStart.getTime()) / 1000); + for (int sec = 0; sec < secondsInDay; sec++) { + StringBuilder line = new StringBuilder(); + line.append(df.format(new Date(dayStart.getTime() + ((long) sec * 1000)))); + for (BreakerEnergyArchive b : CollectionUtils.makeNotNull(day.getBreakers())) { + line.append(","); + if ((b.getReadings() == null) || (sec * 4 >= b.getReadings().length)) + line.append("NaN"); + else { + ByteBuffer readings = ByteBuffer.wrap(b.getReadings()); + line.append(readings.getFloat(sec * 4)); + } + } + line.append("\n"); + os.write(NullUtils.toByteArray(line.toString())); + } + } + return; + } + if (NullUtils.makeNotNull(path[1]).contains("json")) { + DaoEntity archive = DaoSerializer.fromZipBson(IOUtils.toByteArray(is)); + gout = new GZIPOutputStream(os) {{def.setLevel(Deflater.BEST_SPEED);}}; + jsonWriter = new JsonWriter(new OutputStreamWriter(gout, StandardCharsets.UTF_8), DaoSerializer.JSON_COMPACT_SETTINGS); + new DocumentCodec().encode(jsonWriter, archive.toDocument(), EncoderContext.builder().build()); + return; + } + IOUtils.copy(is, os); + return; + } catch (Exception _e) { + logger.error("Failed to send archive to browser", _e); + redirect(_rep, _req.getContextPath() + "/export"); + return; + } finally { + IOUtils.closeQuietly(is); + IOUtils.closeQuietly(jsonWriter); + IOUtils.closeQuietly(gout); + IOUtils.closeQuietly(os); + } + } + } + List status = Globals.dao.getArchiveStatus(_authCode.getAccountId()); + List months = CollectionUtils.transform(status, _s->new MonthDisplay(DateUtils.format("MMMM yyyy", tz, _s.getMonth()), _s.getMonth().getTime(), (int)_s.getProgress())); + Map model = model(_req, "months", months); + model.put("inprogress", CollectionUtils.anyQualify(months, _m->_m.getProgress() > 0 && _m.getProgress() < 100)); + renderBody(_rep, "export.ftl", model); + } + + @Override + protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { + Date month = new Date(DaoSerializer.toLong(_req.getParameter("month"))); + Globals.dao.archiveMonth(_authCode.getAccountId(), month); + redirect(_rep, "."); + } +} diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/GsoServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/GsoServlet.java new file mode 100644 index 0000000..4aeef81 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/GsoServlet.java @@ -0,0 +1,33 @@ +package com.lanternsoftware.currentmonitor.servlet.console; + +import com.lanternsoftware.currentmonitor.servlet.FreemarkerCMServlet; +import com.lanternsoftware.currentmonitor.util.GoogleAuthHelper; +import com.lanternsoftware.util.NullUtils; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet("/gso") +public class GsoServlet extends FreemarkerCMServlet { + @Override + protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { + render(_rep, "login.ftl", model(_req)); + } + + @Override + protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) { + String code = getRequestPayloadAsString(_req); + if (NullUtils.isNotEmpty(code)) { + String authCode = GoogleAuthHelper.signin(code, null); + if (NullUtils.isNotEmpty(authCode)) { + Cookie authCookie = new Cookie("auth_code", authCode); + authCookie.setMaxAge(157680000); + authCookie.setSecure(true); + _rep.addCookie(authCookie); + _req.getSession().setAttribute("auth_code", authCode); + } + } + } +} diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/LoginServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/LoginServlet.java new file mode 100644 index 0000000..6b5035c --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/LoginServlet.java @@ -0,0 +1,42 @@ +package com.lanternsoftware.currentmonitor.servlet.console; + +import com.lanternsoftware.currentmonitor.context.Globals; +import com.lanternsoftware.currentmonitor.servlet.FreemarkerCMServlet; +import com.lanternsoftware.currentmonitor.util.GoogleAuthHelper; +import com.lanternsoftware.util.DateUtils; +import com.lanternsoftware.util.NullUtils; +import com.lanternsoftware.util.dao.DaoEntity; +import com.lanternsoftware.util.dao.DaoSerializer; +import com.lanternsoftware.util.dao.auth.AuthCode; +import com.lanternsoftware.util.servlet.LanternServlet; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@WebServlet("/login") +public class LoginServlet extends FreemarkerCMServlet { + @Override + protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { + render(_rep, "login.ftl", model(_req)); + } + + @Override + protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) { + String username = _req.getParameter("username"); + String password = _req.getParameter("password"); + String authCode = Globals.dao.authenticateAccount(username, password); + if (NullUtils.isNotEmpty(authCode)) { + Cookie authCookie = new Cookie("auth_code", authCode); + authCookie.setMaxAge(157680000); + authCookie.setSecure(true); + _rep.addCookie(authCookie); + _req.getSession().setAttribute("auth_code", authCode); + redirect(_rep, _req.getContextPath()); + } + render(_rep, "login.ftl", model(_req, "error", "Invalid Credentials")); + } +} diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/LogoutServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/LogoutServlet.java new file mode 100644 index 0000000..0daa954 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/LogoutServlet.java @@ -0,0 +1,27 @@ +package com.lanternsoftware.currentmonitor.servlet.console; + +import com.lanternsoftware.currentmonitor.servlet.FreemarkerCMServlet; +import com.lanternsoftware.currentmonitor.util.GoogleAuthHelper; +import com.lanternsoftware.util.NullUtils; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet("/logout") +public class LogoutServlet extends FreemarkerCMServlet { + @Override + protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { + _req.getSession().removeAttribute("auth_code"); + Cookie authCookie = new Cookie("auth_code", ""); + authCookie.setMaxAge(0); + authCookie.setSecure(true); + _rep.addCookie(authCookie); + redirect(_rep, _req.getContextPath()); + } + + @Override + protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) { + } +} diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/MonthDisplay.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/MonthDisplay.java new file mode 100644 index 0000000..c1779e3 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/MonthDisplay.java @@ -0,0 +1,29 @@ +package com.lanternsoftware.currentmonitor.servlet.console; + +public class MonthDisplay { + public final String name; + public final long date; + public final int progress; + + public MonthDisplay(String _name, long _date, int _progress) { + name = _name; + date = _date; + progress = _progress; + } + + public String getName() { + return name; + } + + public String getFileName() { + return name.replace(" ", "-"); + } + + public String getDate() { + return String.valueOf(date); + } + + public int getProgress() { + return progress; + } +} diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/SecureConsoleServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/SecureConsoleServlet.java new file mode 100644 index 0000000..56c3e52 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/SecureConsoleServlet.java @@ -0,0 +1,48 @@ +package com.lanternsoftware.currentmonitor.servlet.console; + +import com.lanternsoftware.currentmonitor.context.Globals; +import com.lanternsoftware.currentmonitor.servlet.FreemarkerCMServlet; +import com.lanternsoftware.util.CollectionUtils; +import com.lanternsoftware.util.NullUtils; +import com.lanternsoftware.util.dao.DaoSerializer; +import com.lanternsoftware.util.dao.auth.AuthCode; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public abstract class SecureConsoleServlet extends FreemarkerCMServlet { + @Override + protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { + AuthCode code = getAuthCode(_req, _rep); + if (code != null) + get(code, _req, _rep); + } + + protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { + } + + @Override + protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) { + AuthCode code = getAuthCode(_req, _rep); + if (code != null) + post(code, _req, _rep); + } + + private AuthCode getAuthCode(HttpServletRequest _req, HttpServletResponse _rep) { + AuthCode authCode = Globals.dao.decryptAuthCode(DaoSerializer.toString(_req.getSession().getAttribute("auth_code"))); + if (authCode == null) { + Cookie authCookie = CollectionUtils.filterOne(CollectionUtils.asArrayList(_req.getCookies()), _c-> NullUtils.isEqual(_c.getName(), "auth_code")); + if (authCookie != null) + authCode = Globals.dao.decryptAuthCode(authCookie.getValue()); + } + if (authCode == null) { + redirect(_rep, _req.getContextPath() + "/login"); + return null; + } + return authCode; + } + + protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { + } +} diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/util/GoogleAuthHelper.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/util/GoogleAuthHelper.java new file mode 100644 index 0000000..662ed30 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/util/GoogleAuthHelper.java @@ -0,0 +1,42 @@ +package com.lanternsoftware.currentmonitor.util; + +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest; +import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; +import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.gson.GsonFactory; +import com.lanternsoftware.currentmonitor.context.Globals; +import com.lanternsoftware.util.external.LanternFiles; +import com.lanternsoftware.util.ResourceLoader; +import com.lanternsoftware.util.dao.DaoEntity; +import com.lanternsoftware.util.dao.DaoSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.TimeZone; + +public class GoogleAuthHelper { + private static final Logger logger = LoggerFactory.getLogger(GoogleAuthHelper.class); + private static final NetHttpTransport transport = new NetHttpTransport(); + private static final String googleClientId; + private static final String googleClientSecret; + static { + DaoEntity google = DaoSerializer.parse(ResourceLoader.loadFileAsString(LanternFiles.CONFIG_PATH + "google_sso.txt")); + googleClientId = DaoSerializer.getString(google, "id"); + googleClientSecret = DaoSerializer.getString(google, "secret"); + } + + public static String signin(String _code, TimeZone _tz) { + try { + GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(transport, new GsonFactory(), "https://oauth2.googleapis.com/token", googleClientId, googleClientSecret, _code, "postmessage").execute(); + if (tokenResponse != null) { + GoogleIdToken idToken = tokenResponse.parseIdToken(); + if (idToken != null) + return Globals.dao.getAuthCodeForEmail(idToken.getPayload().getEmail(), _tz); + } + } catch (Exception _e) { + logger.error("Failed to validate google auth code", _e); + } + return null; + } +} diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/export.ftl b/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/export.ftl new file mode 100644 index 0000000..0aaadf2 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/export.ftl @@ -0,0 +1,50 @@ + + + + + + + <#if inprogress> + Lantern Console + + + + +