]> git.ktnx.net Git - mobile-ledger.git/blobdiff - app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java
more pronounced day/month delimiters in the transaction list
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / async / RetrieveTransactionsTask.java
index aa47d73d403cc456bf6fca05a7046e8dbd421241..0b751b4c6d7df0c94174973ec7a2e449352c9dc9 100644 (file)
 package net.ktnx.mobileledger.async;
 
 import android.annotation.SuppressLint;
-import android.database.sqlite.SQLiteDatabase;
-import android.os.AsyncTask;
 import android.os.OperationCanceledException;
 
 import androidx.annotation.NonNull;
 
+import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.RuntimeJsonMappingException;
 
-import net.ktnx.mobileledger.App;
+import net.ktnx.mobileledger.dao.AccountDAO;
+import net.ktnx.mobileledger.dao.TransactionDAO;
+import net.ktnx.mobileledger.db.Account;
+import net.ktnx.mobileledger.db.AccountWithAmounts;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Option;
+import net.ktnx.mobileledger.db.Profile;
+import net.ktnx.mobileledger.db.TransactionWithAccounts;
 import net.ktnx.mobileledger.err.HTTPException;
 import net.ktnx.mobileledger.json.API;
 import net.ktnx.mobileledger.json.AccountListParser;
@@ -36,8 +42,6 @@ import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.LedgerAccount;
 import net.ktnx.mobileledger.model.LedgerTransaction;
 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
-import net.ktnx.mobileledger.model.MobileLedgerProfile;
-import net.ktnx.mobileledger.ui.MainModel;
 import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.NetworkUtil;
 
@@ -52,6 +56,7 @@ import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -60,8 +65,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 
-public class RetrieveTransactionsTask extends
-        AsyncTask<Void, RetrieveTransactionsTask.Progress, RetrieveTransactionsTask.Result> {
+public class RetrieveTransactionsTask extends Thread {
     private static final int MATCHING_TRANSACTIONS_LIMIT = 150;
     private static final Pattern reComment = Pattern.compile("^\\s*;");
     private static final Pattern reTransactionStart = Pattern.compile(
@@ -75,21 +79,16 @@ public class RetrieveTransactionsTask extends
     private static final Pattern reEnd = Pattern.compile("\\bid=\"addmodal\"");
     private static final Pattern reDecimalPoint = Pattern.compile("\\.\\d\\d?$");
     private static final Pattern reDecimalComma = Pattern.compile(",\\d\\d?$");
+    private static final String TAG = "RTT";
     // %3A is '='
     private final Pattern reAccountName =
             Pattern.compile("/register\\?q=inacct%3A([a-zA-Z0-9%]+)\"");
     private final Pattern reAccountValue = Pattern.compile(
             "<span class=\"[^\"]*\\bamount\\b[^\"]*\">\\s*([-+]?[\\d.,]+)(?:\\s+(\\S+))?</span>");
-    private final MainModel mainModel;
-    private final MobileLedgerProfile profile;
-    private final List<LedgerAccount> prevAccounts;
+    private final Profile profile;
     private int expectedPostingsCount = -1;
-    public RetrieveTransactionsTask(@NonNull MainModel mainModel,
-                                    @NonNull MobileLedgerProfile profile,
-                                    List<LedgerAccount> accounts) {
-        this.mainModel = mainModel;
+    public RetrieveTransactionsTask(@NonNull Profile profile) {
         this.profile = profile;
-        this.prevAccounts = accounts;
     }
     private static void L(String msg) {
         //debug("transaction-parser", msg);
@@ -121,25 +120,19 @@ public class RetrieveTransactionsTask extends
             return null;
         }
     }
-    @Override
-    protected void onProgressUpdate(Progress... values) {
-        super.onProgressUpdate(values);
-        Data.backgroundTaskProgress.postValue(values[0]);
+    private void publishProgress(Progress progress) {
+        Data.backgroundTaskProgress.postValue(progress);
     }
-    @Override
-    protected void onPostExecute(Result result) {
-        super.onPostExecute(result);
+    private void finish(Result result) {
         Progress progress = new Progress();
         progress.setState(ProgressState.FINISHED);
         progress.setError(result.error);
-        onProgressUpdate(progress);
+        publishProgress(progress);
     }
-    @Override
-    protected void onCancelled() {
-        super.onCancelled();
+    private void cancel() {
         Progress progress = new Progress();
         progress.setState(ProgressState.FINISHED);
-        onProgressUpdate(progress);
+        publishProgress(progress);
     }
     private void retrieveTransactionListLegacy(List<LedgerAccount> accounts,
                                                List<LedgerTransaction> transactions)
@@ -325,7 +318,7 @@ public class RetrieveTransactionsTask extends
 
                             state = ParserState.EXPECTING_TRANSACTION;
                             L(String.format("transaction %s parsed → expecting transaction",
-                                    transaction.getId()));
+                                    transaction.getLedgerId()));
 
 // sounds like a good idea, but transaction-1 may not be the first one chronologically
 // for example, when you add the initial seeding transaction after entering some others
@@ -340,8 +333,9 @@ public class RetrieveTransactionsTask extends
                             LedgerTransactionAccount lta = parseTransactionAccountLine(line);
                             if (lta != null) {
                                 transaction.addAccount(lta);
-                                L(String.format(Locale.ENGLISH, "%d: %s = %s", transaction.getId(),
-                                        lta.getAccountName(), lta.getAmount()));
+                                L(String.format(Locale.ENGLISH, "%d: %s = %s",
+                                        transaction.getLedgerId(), lta.getAccountName(),
+                                        lta.getAmount()));
                             }
                             else
                                 throw new IllegalStateException(
@@ -384,7 +378,7 @@ public class RetrieveTransactionsTask extends
     }
     private List<LedgerAccount> retrieveAccountList()
             throws IOException, HTTPException, ApiNotSupportedException {
-        final API apiVersion = profile.getApiVersion();
+        final API apiVersion = API.valueOf(profile.getApiVersion());
         if (apiVersion.equals(API.auto)) {
             return retrieveAccountListAnyVersion();
         }
@@ -398,21 +392,20 @@ public class RetrieveTransactionsTask extends
         }
     }
     private List<LedgerAccount> retrieveAccountListAnyVersion()
-            throws HTTPException, ApiNotSupportedException {
+            throws ApiNotSupportedException, IOException, HTTPException {
         for (API ver : API.allVersions) {
             try {
                 return retrieveAccountListForVersion(ver);
             }
-            catch (Exception e) {
+            catch (JsonParseException | RuntimeJsonMappingException e) {
                 Logger.debug("json",
                         String.format(Locale.US, "Error during account list retrieval using API %s",
-                                ver.getDescription()));
+                                ver.getDescription()), e);
             }
 
-            throw new ApiNotSupportedException();
         }
 
-        throw new RuntimeException("This should never be reached");
+        throw new ApiNotSupportedException();
     }
     private List<LedgerAccount> retrieveAccountListForVersion(API version)
             throws IOException, HTTPException {
@@ -427,12 +420,8 @@ public class RetrieveTransactionsTask extends
                 throw new HTTPException(http.getResponseCode(), http.getResponseMessage());
         }
         publishProgress(Progress.indeterminate());
-        SQLiteDatabase db = App.getDatabase();
         ArrayList<LedgerAccount> list = new ArrayList<>();
         HashMap<String, LedgerAccount> map = new HashMap<>();
-        HashMap<String, LedgerAccount> currentMap = new HashMap<>();
-        for (LedgerAccount acc : prevAccounts)
-            currentMap.put(acc.getName(), acc);
         throwIfCancelled();
         try (InputStream resp = http.getInputStream()) {
             throwIfCancelled();
@@ -450,22 +439,17 @@ public class RetrieveTransactionsTask extends
                 list.add(acc);
             }
             throwIfCancelled();
-        }
 
-        // the current account tree may have changed, update the new-to be tree to match
-        for (LedgerAccount acc : list) {
-            LedgerAccount prevData = currentMap.get(acc.getName());
-            if (prevData != null) {
-                acc.setExpanded(prevData.isExpanded());
-                acc.setAmountsExpanded(prevData.amountsExpanded());
-            }
+            Logger.warn("accounts",
+                    String.format(Locale.US, "Got %d accounts using protocol %s", list.size(),
+                            version.getDescription()));
         }
 
         return list;
     }
     private List<LedgerTransaction> retrieveTransactionList()
             throws ParseException, HTTPException, IOException, ApiNotSupportedException {
-        final API apiVersion = profile.getApiVersion();
+        final API apiVersion = API.valueOf(profile.getApiVersion());
         if (apiVersion.equals(API.auto)) {
             return retrieveTransactionListAnyVersion();
         }
@@ -485,16 +469,15 @@ public class RetrieveTransactionsTask extends
             try {
                 return retrieveTransactionListForVersion(ver);
             }
-            catch (Exception | HTTPException e) {
-                Logger.debug("json",
-                        String.format(Locale.US, "Error during account list retrieval using API %s",
-                                ver.getDescription()));
+            catch (Exception e) {
+                Logger.debug("json", String.format(Locale.US,
+                        "Error during transaction list retrieval using API %s",
+                        ver.getDescription()), e);
             }
 
-            throw new ApiNotSupportedException();
         }
 
-        throw new RuntimeException("This should never be reached");
+        throw new ApiNotSupportedException();
     }
     private List<LedgerTransaction> retrieveTransactionListForVersion(API apiVersion)
             throws IOException, ParseException, HTTPException {
@@ -543,23 +526,27 @@ public class RetrieveTransactionsTask extends
             }
 
             throwIfCancelled();
+
+            Logger.warn("transactions",
+                    String.format(Locale.US, "Got %d transactions using protocol %s", trList.size(),
+                            apiVersion.getDescription()));
         }
 
-        // json interface returns transactions if file order and the rest of the machinery
+        // json interface returns transactions in file order and the rest of the machinery
         // expects them in reverse chronological order
         Collections.sort(trList, (o1, o2) -> {
             int res = o2.getDate()
                         .compareTo(o1.getDate());
             if (res != 0)
                 return res;
-            return Long.compare(o2.getId(), o1.getId());
+            return Long.compare(o2.getLedgerId(), o1.getLedgerId());
         });
         return trList;
     }
 
     @SuppressLint("DefaultLocale")
     @Override
-    protected Result doInBackground(Void... params) {
+    public void run() {
         Data.backgroundTaskStarted();
         List<LedgerAccount> accounts;
         List<LedgerTransaction> transactions;
@@ -579,45 +566,48 @@ public class RetrieveTransactionsTask extends
                 transactions = new ArrayList<>();
                 retrieveTransactionListLegacy(accounts, transactions);
             }
-            mainModel.setAndStoreAccountAndTransactionListFromWeb(accounts, transactions);
 
-            return new Result(accounts, transactions);
+            new AccountAndTransactionListSaver(accounts, transactions).start();
+
+            Data.lastUpdateDate.postValue(new Date());
+
+            finish(new Result(null));
         }
         catch (MalformedURLException e) {
             e.printStackTrace();
-            return new Result("Invalid server URL");
+            finish(new Result("Invalid server URL"));
         }
         catch (HTTPException e) {
             e.printStackTrace();
-            return new Result(String.format("HTTP error %d: %s", e.getResponseCode(),
-                    e.getResponseMessage()));
+            finish(new Result(
+                    String.format("HTTP error %d: %s", e.getResponseCode(), e.getMessage())));
         }
         catch (IOException e) {
             e.printStackTrace();
-            return new Result(e.getLocalizedMessage());
+            finish(new Result(e.getLocalizedMessage()));
         }
         catch (RuntimeJsonMappingException e) {
             e.printStackTrace();
-            return new Result(Result.ERR_JSON_PARSER_ERROR);
+            finish(new Result(Result.ERR_JSON_PARSER_ERROR));
         }
         catch (ParseException e) {
             e.printStackTrace();
-            return new Result("Network error");
+            finish(new Result("Network error"));
         }
         catch (OperationCanceledException e) {
-            e.printStackTrace();
-            return new Result("Operation cancelled");
+            Logger.debug("RTT", "Retrieval was cancelled", e);
+            finish(new Result(null));
         }
         catch (ApiNotSupportedException e) {
             e.printStackTrace();
-            return new Result("Server version not supported");
+            finish(new Result("Server version not supported"));
         }
         finally {
             Data.backgroundTaskFinished();
         }
     }
     public void throwIfCancelled() {
-        if (isCancelled())
+        if (isInterrupted())
             throw new OperationCanceledException(null);
     }
     private enum ParserState {
@@ -713,4 +703,55 @@ public class RetrieveTransactionsTask extends
             this.transactions = transactions;
         }
     }
+
+    private class AccountAndTransactionListSaver extends Thread {
+        private final List<LedgerAccount> accounts;
+        private final List<LedgerTransaction> transactions;
+        public AccountAndTransactionListSaver(List<LedgerAccount> accounts,
+                                              List<LedgerTransaction> transactions) {
+            this.accounts = accounts;
+            this.transactions = transactions;
+        }
+        @Override
+        public void run() {
+            AccountDAO accDao = DB.get()
+                                  .getAccountDAO();
+            TransactionDAO trDao = DB.get()
+                                     .getTransactionDAO();
+
+            Logger.debug(TAG, "Preparing account list");
+            final List<AccountWithAmounts> list = new ArrayList<>();
+            for (LedgerAccount acc : accounts) {
+                final AccountWithAmounts a = acc.toDBOWithAmounts();
+                Account existing = accDao.getByNameSync(profile.getId(), acc.getName());
+                if (existing != null) {
+                    a.account.setExpanded(existing.isExpanded());
+                    a.account.setAmountsExpanded(existing.isAmountsExpanded());
+                    a.account.setId(existing.getId()); // not strictly needed, but since we have it
+                    // anyway...
+                }
+
+                list.add(a);
+            }
+            Logger.debug(TAG, "Account list prepared. Storing");
+            accDao.storeAccountsSync(list, profile.getId());
+            Logger.debug(TAG, "Account list stored");
+
+            Logger.debug(TAG, "Preparing transaction list");
+            final List<TransactionWithAccounts> tranList = new ArrayList<>();
+
+            for (LedgerTransaction tr : transactions)
+                tranList.add(tr.toDBO());
+
+            Logger.debug(TAG, "Storing transaction list");
+            trDao.storeTransactionsSync(tranList, profile.getId());
+
+            Logger.debug(TAG, "Transactions stored");
+
+            DB.get()
+              .getOptionDAO()
+              .insertSync(new Option(profile.getId(), Option.OPT_LAST_SCRAPE,
+                      String.valueOf((new Date()).getTime())));
+        }
+    }
 }