]> git.frustrated-labs.net Git - larousseapi/commitdiff
feat: add option to export wordlist to Anki master
authorAlexander Goussas <[email protected]>
Sun, 5 Jul 2026 19:57:35 +0000 (14:57 -0500)
committerAlexander Goussas <[email protected]>
Sun, 5 Jul 2026 19:57:35 +0000 (14:57 -0500)
src/main/java/net/frustratedfunctor/ui/UiResource.java
src/main/resources/templates/pub/index.html

index 589b11dbb36b2cb493855b58269b84940d964712..3f9b039f01927891e43c1b35fce38a4276ba5f10 100644 (file)
@@ -8,13 +8,21 @@ import jakarta.ws.rs.POST;
 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 {
@@ -47,4 +55,55 @@ 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();
+    }
+
 }
index 05d60c8ee26b914af1371f817725729c00c6fc5c..377a783c18af55d42d61bd36a3fb5b6f24fd650e 100644 (file)
 <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;"