Compare commits
7 Commits
838069e1b9
...
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 521fea51f5 | |||
| ef575cdbd2 | |||
| f86934472c | |||
| 9f7ae95436 | |||
| b9072fc8e8 | |||
| 7763284ae3 | |||
| 0873432a96 |
8
pom.xml
8
pom.xml
@@ -7,7 +7,7 @@
|
||||
|
||||
<groupId>com.calendar</groupId>
|
||||
<artifactId>vaccalc</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>1.1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>VacCalc</name>
|
||||
@@ -29,6 +29,12 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
||||
@@ -3,18 +3,22 @@ package net.themusicinnoise.vaccalc;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PointEngine {
|
||||
private double defaultPoints;
|
||||
private static final Pattern DEFAULT_PATTERN = Pattern.compile("^default\\s+(\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
static private class PointRule {
|
||||
private enum RuleType {
|
||||
@@ -23,9 +27,9 @@ public class PointEngine {
|
||||
DATE,
|
||||
}
|
||||
|
||||
static final Pattern DOW_PATTERN = Pattern.compile("dow=(sun|mon|tue|wed|thu|fri|sat) (\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE);
|
||||
static final Pattern MONTH_PATTERN = Pattern.compile("m=(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE);
|
||||
static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4}-\\d{2}-\\d{2}) (\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern DOW_PATTERN = Pattern.compile("^dow=(sun|mon|tue|wed|thu|fri|sat)\\s+(\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern MONTH_PATTERN = Pattern.compile("^m=(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\\s+(\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern DATE_PATTERN = Pattern.compile("^(\\d{4}-\\d{2}-\\d{2})\\s+(\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private RuleType type;
|
||||
private DayOfWeek dow;
|
||||
@@ -77,15 +81,13 @@ public class PointEngine {
|
||||
}
|
||||
|
||||
public void importPointsFile(File pointsFile) {
|
||||
Pattern defaultPattern = Pattern.compile("default (\\d+\\.\\d+)");
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(pointsFile))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.isEmpty() || line.charAt(0) == '#')
|
||||
continue;
|
||||
|
||||
Matcher defaultMatcher = defaultPattern.matcher(line);
|
||||
Matcher defaultMatcher = DEFAULT_PATTERN.matcher(line);
|
||||
if (defaultMatcher.find()) {
|
||||
defaultPoints = Double.parseDouble(defaultMatcher.group(1));
|
||||
} else {
|
||||
@@ -107,4 +109,41 @@ public class PointEngine {
|
||||
}
|
||||
return defaultPoints;
|
||||
}
|
||||
|
||||
public void exportSelectedDates(File exportFile, Set<LocalDate> selectedDates) throws IOException {
|
||||
try (FileWriter writer = new FileWriter(exportFile)) {
|
||||
selectedDates.stream()
|
||||
.sorted()
|
||||
.forEach(date -> {
|
||||
try {
|
||||
writer.write(String.format("%s%n", date));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Set<LocalDate> importSelectedDates(File importFile) throws IOException {
|
||||
Set<LocalDate> dates = new HashSet<>();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(importFile))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty() || line.charAt(0) == '#')
|
||||
continue;
|
||||
|
||||
try {
|
||||
LocalDate date = LocalDate.parse(line, formatter);
|
||||
dates.add(date);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Invalid date format in line: '" + line + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dates;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,12 @@ package net.themusicinnoise.vaccalc;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class VacCalc extends JFrame {
|
||||
private CalendarPanel calendarPanel;
|
||||
@@ -39,6 +44,61 @@ public class VacCalc extends JFrame {
|
||||
}
|
||||
});
|
||||
appMenu.add(importItem);
|
||||
appMenu.addSeparator();
|
||||
JMenuItem exportItem = new JMenuItem("Export selected dates");
|
||||
exportItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
if (calendarPanel.getSelectedDates().isEmpty()) {
|
||||
JOptionPane.showMessageDialog(VacCalc.this, "No dates selected.", "Export Error",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setSelectedFile(new File("vacation_dates.txt"));
|
||||
int ret = fileChooser.showSaveDialog(VacCalc.this);
|
||||
if(ret == JFileChooser.APPROVE_OPTION) {
|
||||
try {
|
||||
pointEngine.exportSelectedDates(fileChooser.getSelectedFile(), calendarPanel.getSelectedDates());
|
||||
JOptionPane.showMessageDialog(VacCalc.this, "Dates exported successfully.", "Export Complete",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
} catch(Exception ex) {
|
||||
ex.printStackTrace();
|
||||
JOptionPane.showMessageDialog(VacCalc.this, ex.getMessage(), "Export Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
appMenu.add(exportItem);
|
||||
JMenuItem importDatesItem = new JMenuItem("Import dates");
|
||||
importDatesItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
int ret = fileChooser.showOpenDialog(VacCalc.this);
|
||||
if(ret == JFileChooser.APPROVE_OPTION) {
|
||||
try {
|
||||
calendarPanel.clearSelection();
|
||||
Set<LocalDate> importedDates = pointEngine.importSelectedDates(fileChooser.getSelectedFile());
|
||||
if (importedDates.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(VacCalc.this, "No valid dates found in file.", "Import Warning",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
calendarPanel.setSelectedDates(importedDates);
|
||||
JOptionPane.showMessageDialog(VacCalc.this, "Imported " + importedDates.size() + " dates.", "Import Complete",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
} catch(Exception ex) {
|
||||
ex.printStackTrace();
|
||||
JOptionPane.showMessageDialog(VacCalc.this, ex.getMessage(), "Import Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
appMenu.add(importDatesItem);
|
||||
appMenu.addSeparator();
|
||||
JMenuItem exitItem = new JMenuItem("Exit");
|
||||
exitItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
@@ -48,6 +108,42 @@ public class VacCalc extends JFrame {
|
||||
});
|
||||
appMenu.add(exitItem);
|
||||
menuBar.add(appMenu);
|
||||
JMenu helpMenu = new JMenu("Help");
|
||||
JMenuItem manualItem = new JMenuItem("Usage Manual");
|
||||
manualItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("manual.html")));
|
||||
String manualText = br.lines().collect(Collectors.joining());
|
||||
JEditorPane textArea = new JEditorPane("text/html", manualText);
|
||||
textArea.setEditable(false);
|
||||
JFrame manualFrame = new JFrame("VacCalc Manual");
|
||||
manualFrame.getContentPane().add(new JScrollPane(textArea));
|
||||
manualFrame.setSize(500, 500);
|
||||
manualFrame.setVisible(true);
|
||||
}
|
||||
});
|
||||
helpMenu.add(manualItem);
|
||||
JMenuItem aboutItem = new JMenuItem("About");
|
||||
aboutItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("about.html")));
|
||||
Properties properties = new Properties();
|
||||
try {
|
||||
properties.load(getClass().getClassLoader().getResourceAsStream("project.properties"));
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to load project properties.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
String aboutText = br.lines().collect(Collectors.joining());
|
||||
aboutText = aboutText.replace("VERSION", properties.getProperty("version"));
|
||||
JOptionPane.showMessageDialog(VacCalc.this, aboutText, "About",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
});
|
||||
helpMenu.add(aboutItem);
|
||||
menuBar.add(helpMenu);
|
||||
setJMenuBar(menuBar);
|
||||
|
||||
pointEngine = new PointEngine();
|
||||
|
||||
7
src/main/resources/about.html
Normal file
7
src/main/resources/about.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<p><b>VacCalc VERSION</b></p>
|
||||
<hr />
|
||||
<p>Copyright © 2026 Nicolás A. Ortega Froysa <nicolas@ortegas.org></p>
|
||||
<p>License: Zlib</p>
|
||||
<p>Home page: <a href="https://code.ortegas.org/nortega/vaccalc" >https://code.ortegas.org/nortega/vaccalc</a></p>
|
||||
</html>
|
||||
59
src/main/resources/manual.html
Normal file
59
src/main/resources/manual.html
Normal file
@@ -0,0 +1,59 @@
|
||||
<html>
|
||||
<h1>VacCalc Manual</h1>
|
||||
<hr />
|
||||
<p>VacCalc assigns points to each day on the basis of the rules found in a
|
||||
file which is imported via the “VacCalc > Import points” menu item. This
|
||||
file must be written manually. What follows are descriptions on how to write
|
||||
this file.</p>
|
||||
<h2>Rules</h2>
|
||||
<p>There are a total of four different kinds of rules possible: default,
|
||||
date, day-of-week, and month. Rules are applied with a priority for the last
|
||||
defined rule, with the exception of the default rule which is only used if
|
||||
no other rule applies.</p>
|
||||
<h3>Default Rule</h3>
|
||||
<p>Definition: <code>default <i>points</i></code></p>
|
||||
<p>The default rule establishes the default point value for every day of the
|
||||
year. E.g. <code>default 1.0</code> sets the default points to
|
||||
<code>1.0</code>.
|
||||
<h3>Date Rule</h3>
|
||||
<p>Definition: <code><i>YYYY</i>-<i>MM</i>-<i>DD</i> <i>points</i></code></p>
|
||||
<p>A date rule specifies the points which a specific date has. E.g.
|
||||
<code>2026-12-24 0.5</code> would set Christmas Eve (December
|
||||
24<sup>th</sup>) of 2026 to <code>0.5</code> points</p>
|
||||
<h3>Day-Of-Week Rule</h3>
|
||||
<p>Definition: <code>dow=<i>day</i> <i>points</i></p>
|
||||
<p>A day-of-week rule specifies the points for all days of the year which
|
||||
coincide with a particular day of the week. This day of the week is
|
||||
specified using the three-letter format, such as <code>Fri</code> for
|
||||
Friday. E.g. <code>dow=Fri 0.8</code> would set all Fridays of the year to
|
||||
<code>0.8</code> points.</p>
|
||||
<h3>Month Rule</h3>
|
||||
<p>Definition: <code>m=<i>month</i> <i>points</i></p>
|
||||
<p>A month rule establishes the same points for every day of a given month.
|
||||
E.g. <code>m=Aug 0.8</code> sets all the days of the month of August to
|
||||
<code>0.8</code> points.
|
||||
<h3>Zero Point Rules</h3>
|
||||
<p>Rules that have zero points allocated to them (i.e. <code>0.0</code>) are
|
||||
considered <em>free days</em>, and as such are disabled. This could be
|
||||
assigned to days such as Christmas or the Weekends (Saturday and
|
||||
Sunday).</p>
|
||||
<h3>Comments</h3>
|
||||
<p>Definition: <code># <i>comment</i></p>
|
||||
<p>Comments are supported in single lines starting with the <code>#</code>
|
||||
character. Everything after that is ignored by the parser.</p>
|
||||
<h3>Example</h3>
|
||||
<code>
|
||||
default 1.0<br />
|
||||
dow=Fri 0.825<br />
|
||||
m=Jul 0.825<br />
|
||||
m=Aug 0.825<br />
|
||||
2026-12-24 0.5<br />
|
||||
2026-12-31 0.5<br />
|
||||
<br />
|
||||
# Weekends and Holidays<br />
|
||||
dow=Sun 0.0<br />
|
||||
dow=Sat 0.0<br />
|
||||
2026-01-01 0.0<br />
|
||||
2026-12-25 0.0
|
||||
</code>
|
||||
</html>
|
||||
1
src/main/resources/project.properties
Normal file
1
src/main/resources/project.properties
Normal file
@@ -0,0 +1 @@
|
||||
version=${project.version}
|
||||
Reference in New Issue
Block a user