diff --git a/recorder/.gitignore b/recorder/.gitignore new file mode 100644 index 0000000..2c402c4 --- /dev/null +++ b/recorder/.gitignore @@ -0,0 +1,40 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store + +.idea/ \ No newline at end of file diff --git a/recorder/pom.xml b/recorder/pom.xml new file mode 100644 index 0000000..3ae0933 --- /dev/null +++ b/recorder/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + com.github.andrewlalis.running-every-day + recorder + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + + + + + org.xerial + sqlite-jdbc + 3.41.2.1 + + + com.formdev + flatlaf + 3.1 + + + + \ No newline at end of file diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/RecorderApp.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/RecorderApp.java new file mode 100644 index 0000000..7bfde79 --- /dev/null +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/RecorderApp.java @@ -0,0 +1,23 @@ +package com.github.andrewlalis.running_every_day; + +import com.formdev.flatlaf.FlatLightLaf; +import com.github.andrewlalis.running_every_day.data.DataSource; +import com.github.andrewlalis.running_every_day.view.RecorderAppWindow; + +import java.sql.SQLException; + +/** + * The main application entrypoint. + */ +public class RecorderApp { + public static void main(String[] args) { + try (var dataSource = new DataSource("jdbc:sqlite:runs.db")) { + FlatLightLaf.setup(); + var window = new RecorderAppWindow(dataSource); + window.setVisible(true); + } catch (SQLException e) { + System.err.println("An SQL error occurred: " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/DataSource.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/DataSource.java new file mode 100644 index 0000000..456754d --- /dev/null +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/DataSource.java @@ -0,0 +1,48 @@ +package com.github.andrewlalis.running_every_day.data; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class DataSource implements AutoCloseable { + private final Connection conn; + + public DataSource(String url) throws SQLException { + this.conn = DriverManager.getConnection(url); + this.initSchemaIfNeeded(); + } + + public void close() throws SQLException { + this.conn.close(); + } + + private void initSchemaIfNeeded() throws SQLException { + boolean shouldInitSchema; + try ( + var stmt = this.conn.prepareStatement("SELECT name FROM sqlite_master WHERE type='table' AND name='run'"); + var rs = stmt.executeQuery() + ) { + shouldInitSchema = !rs.next(); + } + if (shouldInitSchema) { + try (var stmt = this.conn.createStatement()) { + stmt.execute(readResource("schema.sql")); + } + } + } + + private static String readResource(String name) { + try (var in = DataSource.class.getClassLoader().getResourceAsStream(name)) { + if (in == null) { + throw new RuntimeException("Missing resource: " + name); + } + return new String(in.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/RunRecord.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/RunRecord.java new file mode 100644 index 0000000..8c99fed --- /dev/null +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/data/RunRecord.java @@ -0,0 +1,26 @@ +package com.github.andrewlalis.running_every_day.data; + +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalTime; + +/** + * An immutable record that represents an entry in the `run` table. + * @param id The unique id of the record. + * @param date The date of the record. + * @param startTime The time at which the record started. + * @param distanceKm The distance in kilometers. + * @param duration The duration of the run. + * @param weightKg The body-weight recorded for the run. + * @param comment Any comments for the run. + */ +public record RunRecord( + long id, + LocalDate date, + LocalTime startTime, + BigDecimal distanceKm, + Duration duration, + BigDecimal weightKg, + String comment +) {} diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RecorderAppWindow.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RecorderAppWindow.java new file mode 100644 index 0000000..26a22ca --- /dev/null +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RecorderAppWindow.java @@ -0,0 +1,17 @@ +package com.github.andrewlalis.running_every_day.view; + +import com.github.andrewlalis.running_every_day.data.DataSource; + +import javax.swing.*; +import java.awt.*; + +public class RecorderAppWindow extends JFrame { + public RecorderAppWindow(DataSource dataSource) { + super("Run-Recorder"); + this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + // TODO: Build UI + this.setPreferredSize(new Dimension(800, 600)); + this.pack(); + this.setLocationRelativeTo(null); + } +} diff --git a/recorder/src/main/resources/schema.sql b/recorder/src/main/resources/schema.sql new file mode 100644 index 0000000..68aa59c --- /dev/null +++ b/recorder/src/main/resources/schema.sql @@ -0,0 +1,9 @@ +CREATE TABLE run ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + date TEXT NOT NULL, + start_time TEXT, + distance INTEGER, + duration INTEGER, + weight INTEGER, + comment TEXT +) \ No newline at end of file