import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
import net.frustratedfunctor.definitions.WordDefinitionService;
import net.frustratedfunctor.translations.WordTranslationService;
+import org.jboss.resteasy.reactive.PartType;
+import org.jboss.resteasy.reactive.RestForm;
+import org.jboss.resteasy.reactive.multipart.FileUpload;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
@Path("/ui")
public class UiResource {
return index.data("translationsDefinitions", data);
}
+ // TODO: Use SSE to run this in the background and notify client when process is done.
+
+ @Path("ankiExport")
+ @POST
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ public Response exportToAnki(
+ @RestForm("lang") String lang,
+ @PartType(MediaType.MULTIPART_FORM_DATA)
+ @RestForm("anki-export") FileUpload fileUpload
+ ) throws Exception {
+ final var file = fileUpload.uploadedFile().toFile();
+ final var sb = new StringBuilder(
+ """
+ #separator:Tab
+ #html:true
+ #notetype:Basic
+ #deck:%s
+ """.formatted("CHANGEME") // TODO: Allow customizing deck name.
+ );
+
+ try (
+ final var reader = new InputStreamReader(new FileInputStream(file));
+ final var br = new BufferedReader(reader)
+ ) {
+ final var words = br.lines().toList();
+ for (final var word : words) {
+ final var def = definitions.define(word, lang);
+ sb.append(def.word())
+ .append("\t")
+ .append("<ul>")
+ .append(
+ def.definitions()
+ .stream()
+ .limit(3)
+ .map("<li>%s</li>"::formatted)
+ .collect(Collectors.joining())
+ )
+ .append("</ul>")
+ .append("\n");
+ Thread.sleep(500);
+ }
+ }
+
+ final var contents = sb.toString();
+ return Response.ok()
+ .header("Content-Disposition", "attachment; filename=\"anki.txt\"")
+ .header("Content-Length", contents.length())
+ .entity(contents)
+ .build();
+ }
+
}
<main class="">
<h1>Frustrated Langs</h1>
+ <div class="card">
+ <form method="post" action="/ui/ankiExport" enctype=multipart/form-data>
+ <small>Upload file to export to Anki.</small>
+ <label for="lang" class="stack">
+ Language
+ <select id="lang" name="lang" style="margin-bottom: 5px;">
+ <option value="fr">French</option>
+ <option value="de">German</option>
+ </select>
+ </label>
+ <label for="anki-export" class="stack">
+ File
+ <input type="file" id="anki-export" name="anki-export"/>
+ </label>
+ <input type="submit" value="Export" class="stack"/>
+ </form>
+ </div>
+
<div class="card">
<small
style="margin-bottom: 3px;"