mirror of
https://github.com/zyphlar/LanternPowerMonitor.git
synced 2024-03-08 14:07:47 +00:00
Allow exporting all data in bson, json, or csv formats.
This commit is contained in:
Binary file not shown.
Binary file not shown.
+6
-2
@@ -28,11 +28,15 @@ 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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -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<Account> accounts = dao.getProxy().queryAll(Account.class);
|
||||
|
||||
+3
-3
@@ -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)
|
||||
|
||||
+12
@@ -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<HubPowerMinute> 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<ArchiveStatus> getArchiveStatus(int _accountId);
|
||||
|
||||
DateRange getMonitoredDateRange(int _accountId);
|
||||
|
||||
BreakerConfig getConfig(int _accountId);
|
||||
BreakerConfig getMergedConfig(AuthCode _authCode);
|
||||
|
||||
+217
-29
@@ -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<HubPowerMinute> 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<Breaker> 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<Integer, Integer> breakerKeys = CollectionUtils.transformToMap(config.getAllBreakers(), Breaker::getIntKey, _b -> Breaker.intKey(_b.getPanel(), _b.getType() == BreakerType.DOUBLE_POLE_BOTTOM ? _b.getSpace() - 2 : _b.getSpace()));
|
||||
Map<Integer, List<Float>> minuteReadings = new HashMap<>();
|
||||
MonthlyEnergyArchive archive = new MonthlyEnergyArchive();
|
||||
archive.setAccountId(_accountId);
|
||||
archive.setMonth(start);
|
||||
List<DailyEnergyArchive> days = new ArrayList<>();
|
||||
archive.setDays(days);
|
||||
while (start.before(end)) {
|
||||
Map<Integer, byte[]> dayReadings = new HashMap<>();
|
||||
Date dayEnd = DateUtils.addDays(start, 1, tz);
|
||||
int minute = 0;
|
||||
int bytesInDay = (int) (4 * DateUtils.diffInSeconds(start, dayEnd));
|
||||
Iterator<HubPowerMinute> 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<Float> 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<BreakerEnergyArchive> breakerEnergies = new ArrayList<>();
|
||||
for (Entry<Integer, byte[]> 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<Integer, List<Float>> _minuteReadings, Map<Integer, byte[]> _dayReadings) {
|
||||
for (Entry<Integer, List<Float>> 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<ArchiveStatus> getArchiveStatus(int _accountId) {
|
||||
Map<Date, ArchiveStatus> 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<HubPowerMinute> 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<BillingRate> _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<String, BillingRate> 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<Integer> _auxAcctIds) {
|
||||
|
||||
+16
@@ -194,10 +194,26 @@ public class Breaker implements IIdentical<Breaker> {
|
||||
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;
|
||||
}
|
||||
|
||||
+4
@@ -30,6 +30,10 @@ public class BreakerPowerMinute {
|
||||
return Breaker.key(panel, space);
|
||||
}
|
||||
|
||||
public int breakerIntKey() {
|
||||
return Breaker.intKey(panel, space);
|
||||
}
|
||||
|
||||
public List<Float> getReadings() {
|
||||
return readings;
|
||||
}
|
||||
|
||||
+50
@@ -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;
|
||||
}
|
||||
}
|
||||
+34
@@ -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;
|
||||
}
|
||||
}
|
||||
+18
@@ -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<BreakerEnergyArchive> breakers;
|
||||
|
||||
public List<BreakerEnergyArchive> getBreakers() {
|
||||
return breakers;
|
||||
}
|
||||
|
||||
public void setBreakers(List<BreakerEnergyArchive> _breakers) {
|
||||
breakers = _breakers;
|
||||
}
|
||||
}
|
||||
+53
@@ -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<DailyEnergyArchive> 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<DailyEnergyArchive> getDays() {
|
||||
return days;
|
||||
}
|
||||
|
||||
public void setDays(List<DailyEnergyArchive> _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);
|
||||
}
|
||||
}
|
||||
+44
@@ -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<ArchiveStatus>
|
||||
{
|
||||
@Override
|
||||
public Class<ArchiveStatus> getSupportedClass()
|
||||
{
|
||||
return ArchiveStatus.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DaoProxyType> 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;
|
||||
}
|
||||
}
|
||||
+43
@@ -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<BreakerEnergyArchive>
|
||||
{
|
||||
@Override
|
||||
public Class<BreakerEnergyArchive> getSupportedClass()
|
||||
{
|
||||
return BreakerEnergyArchive.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DaoProxyType> 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;
|
||||
}
|
||||
}
|
||||
+40
@@ -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<DailyEnergyArchive>
|
||||
{
|
||||
@Override
|
||||
public Class<DailyEnergyArchive> getSupportedClass()
|
||||
{
|
||||
return DailyEnergyArchive.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DaoProxyType> 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;
|
||||
}
|
||||
}
|
||||
+45
@@ -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<MonthlyEnergyArchive>
|
||||
{
|
||||
@Override
|
||||
public Class<MonthlyEnergyArchive> getSupportedClass()
|
||||
{
|
||||
return MonthlyEnergyArchive.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DaoProxyType> 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;
|
||||
}
|
||||
}
|
||||
+4
@@ -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
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
+2
-26
@@ -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());
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+23
@@ -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<String, Object> _mapModel) {
|
||||
_mapModel.put("body", FreemarkerUtil.render(getFreemarkerConfig(), _sHtmlResourceKey, _mapModel));
|
||||
render(_rep, "frame.ftl", _mapModel);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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"));
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-2
@@ -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) {
|
||||
|
||||
+4
-3
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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"));
|
||||
+3
-3
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
+19
@@ -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");
|
||||
}
|
||||
}
|
||||
+140
@@ -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<Integer, Breaker> 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<ArchiveStatus> status = Globals.dao.getArchiveStatus(_authCode.getAccountId());
|
||||
List<MonthDisplay> months = CollectionUtils.transform(status, _s->new MonthDisplay(DateUtils.format("MMMM yyyy", tz, _s.getMonth()), _s.getMonth().getTime(), (int)_s.getProgress()));
|
||||
Map<String, Object> 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, ".");
|
||||
}
|
||||
}
|
||||
+33
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -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"));
|
||||
}
|
||||
}
|
||||
+27
@@ -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) {
|
||||
}
|
||||
}
|
||||
+29
@@ -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;
|
||||
}
|
||||
}
|
||||
+48
@@ -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) {
|
||||
}
|
||||
}
|
||||
+42
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<#if inprogress><meta http-equiv="refresh" content="1"></#if>
|
||||
<title>Lantern Console</title>
|
||||
<link href="${link_prefix}/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="${link_prefix}bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid login-container">
|
||||
<div class="row">
|
||||
<div class="col-1 col-lg-3 col-4k-4"></div>
|
||||
<div class="col-10 col-lg-6 col-4k-4">
|
||||
<div class="row mt-3">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<#list months as month>
|
||||
<#if month.progress == 0>
|
||||
<form method="POST">
|
||||
</#if>
|
||||
<tr>
|
||||
<td>${month.name!}</td>
|
||||
<#if month.progress == 0>
|
||||
<td><input type="hidden" name="month" value="${month.date}"/><input type="submit" class="btn-primary border-none px-2 py-1" value="Export"/></td>
|
||||
<#elseif month.progress == 1>
|
||||
<td>Queued</td>
|
||||
<#elseif month.progress < 100>
|
||||
<td>Progress ${month.progress!}%</td>
|
||||
<#else>
|
||||
<td>
|
||||
<a href="export/${month.date}/${month.fileName!}.bson.zip" class="btn-primary border-none text-decoration-none p-2" download>BSON</a>
|
||||
<a href="export/${month.date}/${month.fileName!}.json.zip" class="btn-primary border-none text-decoration-none p-2" download>JSON</a>
|
||||
<a href="export/${month.date}/${month.fileName!}.csv.zip" class="btn-primary border-none text-decoration-none p-2" download>CSV</a>
|
||||
</td>
|
||||
</#if>
|
||||
</tr>
|
||||
<#if month.progress == 0>
|
||||
</form>
|
||||
</#if>
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-1 col-lg-3 col-4k-4"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Lantern Power Monitor</title>
|
||||
<link rel="icon" type="image/png" href="${link_prefix}img/favicon.png">
|
||||
<link href="${link_prefix}bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="${link_prefix}bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="container-fluid">
|
||||
<div class="row header-menu py-2">
|
||||
<div class="col-2"></div>
|
||||
<div class="col-auto"><img class="img-fluid" alt="Logo" src="${link_prefix}img/logo_40.png"/></div>
|
||||
<div class="col"></div>
|
||||
<div class="col-auto mx-2"><a href="${link_prefix}" class="text-decoration-none header-link fmnu1">DATA EXPORT</a></div>
|
||||
<div class="col-auto mx-2"><a href="${link_prefix}logout" class="text-decoration-none header-link fmnu1">LOGOUT</a></div>
|
||||
<div class="col-2"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
${body}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
|
||||
<script src="https://apis.google.com/js/client:platform.js?onload=start" async defer></script>
|
||||
<script>
|
||||
function start() {
|
||||
gapi.load('auth2', function() {
|
||||
auth2 = gapi.auth2.init({client_id: '412929846491-r3uh0t67mpeouicjvlara580i9cfchol.apps.googleusercontent.com', cookie_policy: 'none', redirect_uri: 'postmessage'});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Lantern Power Monitor</title>
|
||||
<link rel="icon" type="image/png" href="${link_prefix}img/favicon.png">
|
||||
<link href="${link_prefix}bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="${link_prefix}bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="container-fluid login-container">
|
||||
<form method="POST">
|
||||
<div class="row">
|
||||
<div class="col-1 col-lg-3 col-4k-4"></div>
|
||||
<div class="col-10 col-lg-6 col-4k-4">
|
||||
<div class="row pt-5">
|
||||
<div class="col-12">
|
||||
<img class="img-fluid" alt='Lantern Logo' src='${link_prefix}img/lantern_cm.png'/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pt-5">
|
||||
<div class="col-1 col-lg-2"></div>
|
||||
<div class="col-10 col-lg-8">
|
||||
<input type="email" name="username" class="login-input" placeholder="email"/>
|
||||
</div>
|
||||
<div class="col-1 col-lg-2"></div>
|
||||
</div>
|
||||
<div class="row pt-2">
|
||||
<div class="col-1 col-lg-2"></div>
|
||||
<div class="col-10 col-lg-8">
|
||||
<input type="password" name="password" class="login-input" placeholder="password"/>
|
||||
</div>
|
||||
<div class="col-1 col-lg-2"></div>
|
||||
</div>
|
||||
<div class="row pt-2">
|
||||
<div class="col-1 col-lg-2"></div>
|
||||
<div class="col-8 col-lg-6 d-flex"><button type="button" id="signinButton" class="gso"></button></div>
|
||||
<div class="col-2 d-flex justify-content-end">
|
||||
<input type="submit" class="btn-primary border-none px-3" value="Login"/>
|
||||
</div>
|
||||
<div class="col-1 col-lg-2"></div>
|
||||
</div>
|
||||
<div class="row pt-5">
|
||||
<div class="col-1 col-lg-2"></div>
|
||||
<div class="col-10 col-lg-8 error">
|
||||
${error!}
|
||||
</div>
|
||||
<div class="col-1 col-lg-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-1 col-lg-3 col-4k-4"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="login-bkgnd"></div>
|
||||
<script>
|
||||
$('#signinButton').click(function() {
|
||||
auth2.grantOfflineAccess().then(signInCallback);
|
||||
});
|
||||
function signInCallback(authResult) {
|
||||
if (authResult['code']) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'gso',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
contentType: 'application/octet-stream; charset=utf-8',
|
||||
success: function(result) {
|
||||
window.location.replace('./');
|
||||
},
|
||||
processData: false,
|
||||
data: authResult['code']
|
||||
});
|
||||
} else {
|
||||
// There was an error.
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<div class="navy-line"></div>
|
||||
<h4><img class="mr-1" src="${context}/img/logo_40.png">Reset Password</h4>
|
||||
<form class="ml-2" method="POST">
|
||||
<input type="hidden" name="reset_key" value="${key}"/>
|
||||
<input type="hidden" name="reset_key" value="${key!}"/>
|
||||
<div>New Password:</div>
|
||||
<input type="password" name="password"/>
|
||||
<input type="submit" class="btn-primary" value="Submit"/>
|
||||
|
||||
Vendored
+4362
-3215
File diff suppressed because it is too large
Load Diff
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+5
-5
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+5050
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
Vendored
+260
-104
@@ -1,51 +1,150 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.5.0 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2020 The Bootstrap Authors
|
||||
* Copyright 2011-2020 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Bootstrap Reboot v5.1.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors
|
||||
* Copyright 2011-2021 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
:root {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-bg: #fff;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus:not(:focus-visible) {
|
||||
outline: 0 !important;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
background-color: currentColor;
|
||||
border: 0;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
hr:not([size]) {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
@@ -54,12 +153,10 @@ p {
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
abbr[data-bs-original-title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
@@ -70,6 +167,11 @@ address {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
@@ -89,7 +191,7 @@ dt {
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@@ -103,42 +205,39 @@ strong {
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.2em;
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
color: #0d6efd;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
a:hover {
|
||||
color: #0a58ca;
|
||||
}
|
||||
|
||||
a:not([href]):hover {
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -147,59 +246,94 @@ pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
direction: ltr /* rtl:ignore */;
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: #d63384;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.875em;
|
||||
color: #fff;
|
||||
background-color: #212529;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
@@ -213,54 +347,45 @@ textarea {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role="button"] {
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type="button"]:not(:disabled),
|
||||
[type="reset"]:not(:disabled),
|
||||
[type="submit"]:not(:disabled) {
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
@@ -272,33 +397,59 @@ fieldset {
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
[type=search] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: textfield;
|
||||
}
|
||||
|
||||
/* rtl:raw:
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
*/
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
@@ -310,16 +461,21 @@ output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
||||
+1
-1
File diff suppressed because one or more lines are too long
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/bootstrap-reboot.min.css
Vendored
+5
-5
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/bootstrap-reboot.rtl.css
Vendored
+478
@@ -0,0 +1,478 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v5.1.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors
|
||||
* Copyright 2011-2021 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
:root {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-bg: #fff;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
background-color: currentColor;
|
||||
border: 0;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
hr:not([size]) {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-bs-original-title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.2em;
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0d6efd;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
color: #0a58ca;
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
direction: ltr ;
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: #d63384;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.875em;
|
||||
color: #fff;
|
||||
background-color: #212529;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: #6c757d;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: right;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: right;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: textfield;
|
||||
}
|
||||
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
||||
+1
File diff suppressed because one or more lines are too long
+8
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
Vendored
+4866
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+4857
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+8378
-6966
File diff suppressed because it is too large
Load Diff
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+7
-6
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+11198
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+229
@@ -0,0 +1,229 @@
|
||||
.scrollarea {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.fw-semibold { font-weight: 600; }
|
||||
.lh-tight { line-height: 1.25; }
|
||||
|
||||
.menu_link {
|
||||
text-decoration:none!important;
|
||||
}
|
||||
|
||||
.menu_subitem {
|
||||
color:#404040;
|
||||
}
|
||||
|
||||
.menu_item, .menu_subitem:hover, .menu_subitem:focus {
|
||||
background-color: #1a9acc;
|
||||
color:#f0f0f0;
|
||||
}
|
||||
|
||||
.lpm_menu {
|
||||
z-index:1;
|
||||
background:#ffffff;
|
||||
min-height:100%;
|
||||
max-height:100%;
|
||||
}
|
||||
|
||||
#toggle_label {
|
||||
margin-top:0.5rem;
|
||||
display: block;
|
||||
width: 2rem;
|
||||
position:absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.blue, .title {
|
||||
color: #1a9acc;
|
||||
}
|
||||
|
||||
.kill {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.max-w-300 {
|
||||
max-width:min(100%,300px);
|
||||
}
|
||||
|
||||
.max-w-400 {
|
||||
max-width:min(100%,400px);
|
||||
}
|
||||
|
||||
.max-w-500 {
|
||||
max-width:min(100%,500px);
|
||||
}
|
||||
|
||||
.max-w-600 {
|
||||
max-width:min(100%,600px);
|
||||
}
|
||||
|
||||
.max-w-700 {
|
||||
max-width:min(100%,700px);
|
||||
}
|
||||
|
||||
.max-w-800 {
|
||||
max-width:min(100%,800px);
|
||||
}
|
||||
|
||||
.max-w-900 {
|
||||
max-width:min(100%,900px);
|
||||
}
|
||||
|
||||
.max-w-1000 {
|
||||
max-width:min(100%,1000px);
|
||||
}
|
||||
|
||||
.max-w-1200 {
|
||||
max-width:min(100%,1200px);
|
||||
}
|
||||
|
||||
.align-col-right {
|
||||
display: flex !important;
|
||||
justify-content: flex-end !important;
|
||||
padding-right: 1vw !important;
|
||||
}
|
||||
|
||||
.align-col-left {
|
||||
display: flex !important;
|
||||
justify-content: flex-start !important;
|
||||
padding-left: 1vw !important;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
#menu_toggle:checked ~ #toggle_label {
|
||||
position:fixed!important;
|
||||
}
|
||||
|
||||
.lpm_menu {
|
||||
display:none!important;
|
||||
}
|
||||
|
||||
#menu_toggle:checked ~ .lpm_menu {
|
||||
display:block!important;
|
||||
}
|
||||
|
||||
#menu_toggle:checked ~ .lpm_body {
|
||||
display: block!important;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.hsy {
|
||||
margin-top: 2.3rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 3000px) {
|
||||
#menu_toggle:checked ~ .lpm_menu {
|
||||
display:block!important;
|
||||
}
|
||||
|
||||
#menu_toggle:checked ~ .lpm_body {
|
||||
display: block!important;
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.fm1 {
|
||||
font-size: calc(1rem + 1.25vw);
|
||||
}
|
||||
|
||||
.fm2 {
|
||||
font-size: calc(0.7rem + 1vw);
|
||||
}
|
||||
|
||||
.fm3 {
|
||||
font-size: calc(0.8rem + 0.5vw);
|
||||
}
|
||||
|
||||
.fmnu1 {
|
||||
font-size: calc(1rem + 0.5vw);
|
||||
}
|
||||
|
||||
.fmnu2 {
|
||||
font-size: calc(0.8rem + 0.5vw);
|
||||
}
|
||||
|
||||
.login-container {
|
||||
z-index:2;
|
||||
position:absolute;
|
||||
}
|
||||
|
||||
.login-bkgnd {
|
||||
z-index:1;
|
||||
position:absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top:0;
|
||||
left:0;
|
||||
background-image:url('../../img/pcb.png');
|
||||
background-size:cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.login-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #a0a0a0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #1a9acc;
|
||||
border-color: #1a9acc;
|
||||
}
|
||||
|
||||
.btn-primary:hover, .btn-primary:focus{
|
||||
background-color: #20c0ff;
|
||||
border-color: #20c0ff;
|
||||
}
|
||||
|
||||
.header-menu {
|
||||
background-color: #1a9acc;
|
||||
}
|
||||
|
||||
.header-link {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.header-link:hover {
|
||||
color: #d0d0d0;
|
||||
}
|
||||
|
||||
.gso {
|
||||
border-style: none;
|
||||
width: 10rem;
|
||||
height: 2.5rem;
|
||||
background-image: url("../../img/gso.png");
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-color: #0000;
|
||||
}
|
||||
|
||||
.gso:hover {
|
||||
background-color: #0000;
|
||||
}
|
||||
|
||||
.gso:focus {
|
||||
background-image: url("../../img/gso_pressed.png");
|
||||
}
|
||||
|
||||
.border-none {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: darkred;
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
.scrollarea{overflow-y:auto}.fw-semibold{font-weight:600}.lh-tight{line-height:1.25}.menu_link{text-decoration:none!important}.menu_subitem{color:#404040}.menu_item,.menu_subitem:focus,.menu_subitem:hover{background-color:#1a9acc;color:#f0f0f0}.lpm_menu{z-index:1;background:#fff;min-height:100%;max-height:100%}#toggle_label{margin-top:.5rem;display:block;width:2rem;position:absolute;z-index:10}.blue,.title{color:#1a9acc}.kill{color:red}.max-w-300{max-width:min(100%,300px)}.max-w-400{max-width:min(100%,400px)}.max-w-500{max-width:min(100%,500px)}.max-w-600{max-width:min(100%,600px)}.max-w-700{max-width:min(100%,700px)}.max-w-800{max-width:min(100%,800px)}.max-w-900{max-width:min(100%,900px)}.max-w-1000{max-width:min(100%,1000px)}.max-w-1200{max-width:min(100%,1200px)}.align-col-right{display:flex!important;justify-content:flex-end!important;padding-right:1vw!important}.align-col-left{display:flex!important;justify-content:flex-start!important;padding-left:1vw!important}.title{font-size:2em}.sub-title{font-size:1.1em}@media (max-width:991px){#menu_toggle:checked~#toggle_label{position:fixed!important}.lpm_menu{display:none!important}#menu_toggle:checked~.lpm_menu{display:block!important}#menu_toggle:checked~.lpm_body{display:block!important;width:50%}}@media (min-width:992px){.hsy{margin-top:2.3rem!important}}@media (min-width:3000px){#menu_toggle:checked~.lpm_menu{display:block!important}#menu_toggle:checked~.lpm_body{display:block!important;width:50%}}.fm1{font-size:calc(1rem + 1.25vw)}.fm2{font-size:calc(.7rem + 1vw)}.fm3{font-size:calc(.8rem + .5vw)}.fmnu1{font-size:calc(1rem + .5vw)}.fmnu2{font-size:calc(.8rem + .5vw)}.login-bkgnd,.login-container{z-index:2;position:absolute}.login-bkgnd{z-index:1;width:100%;height:100%;top:0;left:0;background-image:url(../../img/pcb.png);background-size:cover;background-position:center}.login-input{width:100%}::placeholder{color:#a0a0a0;opacity:1}.btn-primary{background-color:#1a9acc;border-color:#1a9acc}.btn-primary:focus,.btn-primary:hover{background-color:#20c0ff;border-color:#20c0ff}.header-menu{background-color:#1a9acc}.header-link{color:#f0f0f0}.header-link:hover{color:#d0d0d0}.gso{border-style:none;width:10rem;height:2.5rem;background-image:url(../../img/gso.png);background-position:center;background-size:cover}.gso,.gso:hover{background-color:#0000}.gso:focus{background-image:url(../../img/gso_pressed.png)}.border-none{border-style:none}.error{color:#8b0000}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 167 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 295 KiB |
+2
-4
@@ -3,14 +3,12 @@ package com.lanternsoftware.currentmonitor;
|
||||
import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
|
||||
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.Account;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CreateAccount {
|
||||
public static void main(String[] args) {
|
||||
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||
Account account = new Account();
|
||||
account.setId(1);
|
||||
account.setPassword("*redacted*");
|
||||
|
||||
+2
-2
@@ -1,13 +1,13 @@
|
||||
package com.lanternsoftware.currentmonitor;
|
||||
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.ResourceLoader;
|
||||
import com.lanternsoftware.util.cryptography.AESTool;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import com.lanternsoftware.util.dao.auth.AuthCode;
|
||||
|
||||
public class CreateAuthCode {
|
||||
private static final AESTool aes = new AESTool(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "authKey.dat"));
|
||||
private static final AESTool aes = new AESTool(ResourceLoader.loadFile(LanternFiles.CONFIG_PATH + "authKey.dat"));
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(aes.encryptToBase64(DaoSerializer.toZipBson(new AuthCode(100, null))));
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
package com.lanternsoftware.currentmonitor;
|
||||
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.ResourceLoader;
|
||||
import com.lanternsoftware.util.cryptography.AESTool;
|
||||
|
||||
public class CreateAuthKey {
|
||||
public static void main(String[] args) {
|
||||
ResourceLoader.writeFile(LanternFiles.OPS_PATH + "authKey.dat", AESTool.generateRandomSecretKey().getEncoded());
|
||||
ResourceLoader.writeFile(LanternFiles.CONFIG_PATH + "authKey.dat", AESTool.generateRandomSecretKey().getEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
+31
-16
@@ -4,26 +4,14 @@ import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
|
||||
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
|
||||
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.BreakerPanel;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerPolarity;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.Meter;
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import com.lanternsoftware.util.ResourceLoader;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import com.lanternsoftware.util.IFilter;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CreateBreakers {
|
||||
public static void main(String[] args) {
|
||||
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||
|
||||
/* Breaker bf1 = new Breaker("Solar A", 2, 20, 0, 1, 50, 1.6);
|
||||
bf1.setPolarity(BreakerPolarity.SOLAR);
|
||||
@@ -212,7 +200,6 @@ public class CreateBreakers {
|
||||
Map<Integer, Integer> panelToMeter = CollectionUtils.transformToMap(config.getPanels(), BreakerPanel::getIndex, BreakerPanel::getMeter);
|
||||
CollectionUtils.edit(config.getAllBreakers(), _b->_b.setMeter(DaoSerializer.toInteger(panelToMeter.get(_b.getPanel()))));*/
|
||||
|
||||
BreakerConfig config = dao.getConfig(1);
|
||||
// BreakerConfig config = DaoSerializer.parse(ResourceLoader.loadFileAsString(LanternFiles.OPS_PATH + "breakerconfig_backup_210107.json"), BreakerConfig.class);
|
||||
// ResourceLoader.writeFile(LanternFiles.OPS_PATH + "breakerconfig_backup_210107.json", DaoSerializer.toJson(config));
|
||||
// CollectionUtils.edit(config.getAllBreakerGroups(), _g->{
|
||||
@@ -235,6 +222,34 @@ public class CreateBreakers {
|
||||
// group.setId(String.valueOf(CollectionUtils.getLargest(ids) + 1));
|
||||
// }
|
||||
// }
|
||||
|
||||
BreakerConfig config = dao.getConfig(100);
|
||||
/* TimeZone tz = TimeZone.getTimeZone("America/Chicago");
|
||||
BillingRate summer0 = new BillingRate();
|
||||
summer0.setMeter(-1);
|
||||
summer0.setDayBillingCycleStart(13);
|
||||
summer0.setFlow(GridFlow.BOTH);
|
||||
summer0.setRate(0.13806);
|
||||
summer0.setCurrency(BillingCurrency.DOLLAR);
|
||||
summer0.setBeginEffective(DateUtils.date(5, 16, 2021, tz));
|
||||
summer0.setEndEffective(DateUtils.date(9, 16, 2021, tz));
|
||||
summer0.setRecursAnnually(true);
|
||||
|
||||
BillingRate winter0 = summer0.duplicate();
|
||||
winter0.setMonthKWhEnd(1000);
|
||||
winter0.setRate(0.09703);
|
||||
winter0.setBeginEffective(DateUtils.date(9, 16, 2021, tz));
|
||||
winter0.setEndEffective(DateUtils.date(5, 16, 2022, tz));
|
||||
|
||||
BillingRate winter1 = winter0.duplicate();
|
||||
winter1.setMonthKWhStart(1000);
|
||||
winter1.setMonthKWhEnd(0);
|
||||
winter1.setRate(0.063);
|
||||
config.setBillingRates(CollectionUtils.asArrayList(summer0, winter0, winter1));*/
|
||||
IFilter<Breaker> filter = _b->_b.getName().contains("Water") || _b.getName().contains("Furnace") || _b.getName().contains("Basement HP");
|
||||
CollectionUtils.filter(config.getAllBreakers(), filter).forEach(_b->_b.setMeter(0));
|
||||
CollectionUtils.filter(config.getAllBreakers(), _b->!filter.isFiltered(_b)).forEach(_b->_b.setMeter(1));
|
||||
|
||||
dao.putConfig(config);
|
||||
dao.shutdown();
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
package com.lanternsoftware.currentmonitor;
|
||||
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
|
||||
public class CreateMongoConfig {
|
||||
public static void main(String[] args) {
|
||||
new MongoConfig("localhost", "*redacted*", "*redacted*", "CURRENT_MONITOR").saveToDisk(LanternFiles.OPS_PATH + "mongo.cfg");
|
||||
new MongoConfig("lanternsoftware.com", "*redacted*", "*redacted*", "CURRENT_MONITOR").saveToDisk(LanternFiles.CONFIG_PATH + "mongo.cfg");
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
package com.lanternsoftware.currentmonitor;
|
||||
|
||||
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.generator.DaoSerializerGenerator;
|
||||
import com.lanternsoftware.util.dao.generator.SwiftModelGenerator;
|
||||
|
||||
public class CurrentMonitorSerializers {
|
||||
public static void main(String[] args) {
|
||||
DaoSerializerGenerator.generateSerializers(LanternFiles.SOURCE_PATH + "currentmonitor", true, null);
|
||||
SwiftModelGenerator.generateModel(LanternFiles.SOURCE_PATH + "currentmonitor", LanternFiles.SOURCE_PATH + "iOS");
|
||||
DaoSerializerGenerator.generateSerializers(LanternFiles.SOURCE_CODE_PATH + "currentmonitor", true, null);
|
||||
SwiftModelGenerator.generateModel(LanternFiles.SOURCE_CODE_PATH + "currentmonitor", LanternFiles.SOURCE_CODE_PATH + "iOS");
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -2,21 +2,21 @@ package com.lanternsoftware.currentmonitor;
|
||||
|
||||
import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
|
||||
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupEnergy;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupSummary;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal;
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
|
||||
public class MigrateSummaries {
|
||||
public static void main(String[] args) {
|
||||
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||
// TimeZone tz = TimeZone.getTimeZone("America/Chicago");
|
||||
// List<BreakerGroupEnergy> summaries = dao.getProxy().query(BreakerGroupEnergy.class, null);
|
||||
// CollectionUtils.edit(summaries, _s->CollectionUtils.edit(_s.getAllGroups(), _t->_t.setAccountId(1)));
|
||||
// dao.getProxy().save(summaries);
|
||||
|
||||
dao.getProxy().save(CollectionUtils.transform(dao.getProxy().queryAll(BreakerGroupEnergy.class), BreakerGroupSummary::new));
|
||||
dao.getProxy().save(CollectionUtils.transform(dao.getProxy().queryAll(EnergySummary.class), EnergyTotal::new));
|
||||
|
||||
// List<BreakerPower> readings = null;
|
||||
// while ((readings == null) || !readings.isEmpty()) {
|
||||
|
||||
+14
-10
@@ -2,24 +2,28 @@ package com.lanternsoftware.currentmonitor;
|
||||
|
||||
import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
|
||||
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
|
||||
import com.lanternsoftware.datamodel.currentmonitor.Account;
|
||||
import com.lanternsoftware.util.DateUtils;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class RebuildSummaries {
|
||||
public static void main(String[] args) {
|
||||
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
TimeZone tz = TimeZone.getTimeZone("America/Chicago");
|
||||
dao.rebuildSummaries(100, DateUtils.date(8,3,2021, tz), DateUtils.date(8,5,2021, tz));
|
||||
/* List<Account> accounts = dao.getProxy().queryAll(Account.class);
|
||||
for (int accountId : CollectionUtils.transform(accounts, Account::getId)) {
|
||||
if (accountId != 100)
|
||||
continue;
|
||||
dao.rebuildSummaries(accountId, DateUtils.date(4,21,2021, tz), DateUtils.date(4,22,2021, tz));
|
||||
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||
for (Account a : dao.getProxy().queryAll(Account.class)) {
|
||||
TimeZone tz = DateUtils.fromTimeZoneId(a.getTimezone());
|
||||
// BreakerConfig config = dao.getConfig(a.getId());
|
||||
// BillingPlan plan = new BillingPlan();
|
||||
// plan.setPlanId(1);
|
||||
// plan.setBillingDay(1);
|
||||
// plan.setName("Standard");
|
||||
// plan.setRates(config.getBillingRates());
|
||||
// config.setBillingPlans(CollectionUtils.asArrayList(plan));
|
||||
// dao.putConfig(config);
|
||||
dao.rebuildSummaries(a.getId(), DateUtils.date(12,18,2021,tz), DateUtils.date(12,19,2021,tz));
|
||||
}
|
||||
*/
|
||||
dao.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@ import com.lanternsoftware.datamodel.rules.Rule;
|
||||
import com.lanternsoftware.rules.actions.ActionImpl;
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.DateUtils;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
import org.slf4j.Logger;
|
||||
@@ -54,8 +54,8 @@ public class RulesEngine {
|
||||
|
||||
public RulesEngine() {
|
||||
ServiceLoader.load(ActionImpl.class).forEach(_action->actions.put(_action.getType(), _action));
|
||||
dao = new MongoRulesDataAccess(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
cmDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
dao = new MongoRulesDataAccess(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||
cmDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||
timer = new Timer("RulesEngine Timer");
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import com.lanternsoftware.datamodel.rules.Alert;
|
||||
import com.lanternsoftware.datamodel.rules.FcmDevice;
|
||||
import com.lanternsoftware.datamodel.rules.Rule;
|
||||
import com.lanternsoftware.rules.RulesEngine;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
@@ -25,7 +25,7 @@ public abstract class AbstractAlertAction implements ActionImpl {
|
||||
static {
|
||||
FirebaseMessaging m = null;
|
||||
try {
|
||||
FileInputStream is = new FileInputStream(LanternFiles.OPS_PATH + "google_account_key.json");
|
||||
FileInputStream is = new FileInputStream(LanternFiles.CONFIG_PATH + "google_account_key.json");
|
||||
FirebaseOptions options = FirebaseOptions.builder().setCredentials(GoogleCredentials.fromStream(is)).build();
|
||||
m = FirebaseMessaging.getInstance(FirebaseApp.initializeApp(options));
|
||||
IOUtils.closeQuietly(is);
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.lanternsoftware.dataaccess.rules.MongoRulesDataAccess;
|
||||
import com.lanternsoftware.dataaccess.rules.RulesDataAccess;
|
||||
import com.lanternsoftware.datamodel.rules.Alert;
|
||||
import com.lanternsoftware.datamodel.rules.FcmDevice;
|
||||
import com.lanternsoftware.util.LanternFiles;
|
||||
import com.lanternsoftware.util.external.LanternFiles;
|
||||
import com.lanternsoftware.util.dao.DaoSerializer;
|
||||
import com.lanternsoftware.util.dao.mongo.MongoConfig;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
@@ -18,10 +18,12 @@ import java.io.FileInputStream;
|
||||
|
||||
public class TestSendAlert {
|
||||
public static void main(String[] args) {
|
||||
RulesDataAccess dao = new MongoRulesDataAccess(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
|
||||
RulesDataAccess dao = new MongoRulesDataAccess(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
|
||||
for (FcmDevice d : dao.getFcmDevicesForAccount(100)) {
|
||||
if (!d.getName().contains("Sony"))
|
||||
continue;
|
||||
Alert alert = new Alert();
|
||||
alert.setMessage("Test Alert");
|
||||
alert.setMessage("Garage Door 1 is still open");
|
||||
Message msg = Message.builder().setToken(d.getToken()).putData("payload", DaoSerializer.toBase64ZipBson(alert)).putData("payloadClass", Alert.class.getCanonicalName()).build();
|
||||
try {
|
||||
FileInputStream is = new FileInputStream("d:\\zwave\\firebase\\account_key.json");
|
||||
|
||||
+22
-8
@@ -607,29 +607,43 @@ public class CollectionUtils {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<V>> Map<V, T> transformToSortedMap(Collection<T> _coll, ITransformer<? super T, V> _transformer) {
|
||||
return transformToMap(_coll, _transformer, new TreeMap<>());
|
||||
}
|
||||
|
||||
public static <T, V> Map<V, T> transformToMap(Collection<T> _coll, ITransformer<? super T, V> _transformer) {
|
||||
Map<V, T> mapValues = new HashMap<>();
|
||||
return transformToMap(_coll, _transformer, new HashMap<>());
|
||||
}
|
||||
|
||||
public static <T, V> Map<V, T> transformToMap(Collection<T> _coll, ITransformer<? super T, V> _transformer, Map<V, T> _map) {
|
||||
if ((_coll == null) || (_transformer == null))
|
||||
return mapValues;
|
||||
return _map;
|
||||
for (T t : _coll) {
|
||||
V v = _transformer.transform(t);
|
||||
if (v != null)
|
||||
mapValues.put(v, t);
|
||||
_map.put(v, t);
|
||||
}
|
||||
return mapValues;
|
||||
return _map;
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<V>, U> Map<V, U> transformToSortedMap(Collection<T> _coll, ITransformer<? super T, V> _keyTrans, ITransformer<? super T, U> _valTrans) {
|
||||
return transformToMap(_coll, _keyTrans, _valTrans, new TreeMap<>());
|
||||
}
|
||||
|
||||
public static <T, V, U> Map<V, U> transformToMap(Collection<T> _coll, ITransformer<? super T, V> _keyTrans, ITransformer<? super T, U> _valTrans) {
|
||||
Map<V, U> mapValues = new HashMap<>();
|
||||
return transformToMap(_coll, _keyTrans, _valTrans, new HashMap<>());
|
||||
}
|
||||
|
||||
public static <T, V, U> Map<V, U> transformToMap(Collection<T> _coll, ITransformer<? super T, V> _keyTrans, ITransformer<? super T, U> _valTrans, Map<V, U> _map) {
|
||||
if ((_coll == null) || (_keyTrans == null) || (_valTrans == null))
|
||||
return mapValues;
|
||||
return _map;
|
||||
for (T t : _coll) {
|
||||
V v = _keyTrans.transform(t);
|
||||
U u = _valTrans.transform(t);
|
||||
if ((v != null) && (u != null))
|
||||
mapValues.put(v, u);
|
||||
_map.put(v, u);
|
||||
}
|
||||
return mapValues;
|
||||
return _map;
|
||||
}
|
||||
|
||||
public static <T, V> Map<V, List<T>> transformToMultiMap(Collection<T> _coll, ITransformer<? super T, V> _transformer) {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.lanternsoftware.util;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class DateRange {
|
||||
private Date start;
|
||||
private Date end;
|
||||
|
||||
public DateRange() {
|
||||
}
|
||||
|
||||
public DateRange(Date _start, Date _end) {
|
||||
start = _start;
|
||||
end = _end;
|
||||
}
|
||||
|
||||
public Date getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(Date _start) {
|
||||
start = _start;
|
||||
}
|
||||
|
||||
public Date getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(Date _end) {
|
||||
end = _end;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user