X-Git-Url: https://git.ktnx.net/?p=mobile-ledger.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fnet%2Fktnx%2Fmobileledger%2Fasync%2FRetrieveTransactionsTask.java;h=4da02e823111a070efa473edcbdf8c82ab8521b3;hp=c5850512920312992243bff315ab379cef89e231;hb=e0b2d33c159281741f3a6e30975d58040a8ab879;hpb=b294dd0a2a66ba78449eea3e15df59bb5f1198f4 diff --git a/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java b/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java index c5850512..4da02e82 100644 --- a/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java +++ b/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java @@ -18,7 +18,6 @@ package net.ktnx.mobileledger.async; import android.annotation.SuppressLint; -import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; import android.os.OperationCanceledException; @@ -29,6 +28,7 @@ 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.activity.MainActivity; import net.ktnx.mobileledger.ui.transaction_list.TransactionListViewModel; import net.ktnx.mobileledger.utils.MLDB; @@ -45,34 +45,34 @@ import java.net.MalformedURLException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; +import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class RetrieveTransactionsTask extends - AsyncTask { - public static final int MATCHING_TRANSACTIONS_LIMIT = 50; - private static final Pattern transactionStartPattern = Pattern.compile("([\\d.-]+)"); - private static final Pattern transactionDescriptionPattern = +public class RetrieveTransactionsTask + extends AsyncTask { + private static final int MATCHING_TRANSACTIONS_LIMIT = 50; + private static final Pattern reComment = Pattern.compile("^\\s*;"); + private static final Pattern reTransactionStart = Pattern.compile("([\\d.-]+)"); + private static final Pattern reTransactionDescription = Pattern.compile(" contextRef; - protected int error; + private static final Pattern reTransactionDetails = + Pattern.compile("^\\s+(\\S[\\S\\s]+\\S)\\s\\s+([-+]?\\d[\\d,.]*)(?:\\s+(\\S+)$)?"); + private static final Pattern reEnd = Pattern.compile("\\bid=\"addmodal\""); + private WeakReference contextRef; + private int error; // %3A is '=' - Pattern account_name_re = Pattern.compile("/register\\?q=inacct%3A([a-zA-Z0-9%]+)\""); - Pattern account_value_re = Pattern.compile( - "\\s*([-+]?[\\d.,]+)(?:\\s+(\\S+))?"); - Pattern tr_end_re = Pattern.compile(""); - Pattern descriptions_line_re = Pattern.compile("\\bdescriptionsSuggester\\s*=\\s*new\\b"); - Pattern description_items_re = Pattern.compile("\"value\":\"([^\"]+)\""); private boolean success; + private Pattern reAccountName = Pattern.compile("/register\\?q=inacct%3A([a-zA-Z0-9%]+)\""); + private Pattern reAccountValue = Pattern.compile( + "\\s*([-+]?[\\d.,]+)(?:\\s+(\\S+))?"); public RetrieveTransactionsTask(WeakReference contextRef) { this.contextRef = contextRef; } - private static final void L(String msg) { + private static void L(String msg) { Log.d("transaction-parser", msg); } @Override @@ -105,16 +105,18 @@ public class RetrieveTransactionsTask extends } @SuppressLint("DefaultLocale") @Override - protected Void doInBackground(Params... params) { + protected Void doInBackground(Void... params) { + MobileLedgerProfile profile = Data.profile.get(); Progress progress = new Progress(); int maxTransactionId = Progress.INDETERMINATE; success = false; ArrayList accountList = new ArrayList<>(); + HashMap accountNames = new HashMap<>(); LedgerAccount lastAccount = null; + boolean onlyStarred = Data.optShowOnlyStarred.get(); Data.backgroundTaskCount.incrementAndGet(); try { - HttpURLConnection http = - NetworkUtil.prepare_connection(params[0].getBackendPref(), "journal"); + HttpURLConnection http = NetworkUtil.prepareConnection("journal"); http.setAllowUserInteraction(false); publishProgress(progress); MainActivity ctx = getContext(); @@ -142,6 +144,12 @@ public class RetrieveTransactionsTask extends while ((line = buf.readLine()) != null) { throwIfCancelled(); Matcher m; + m = reComment.matcher(line); + if (m.find()) { + // TODO: comments are ignored for now + Log.v("transaction-parser", "Ignoring comment"); + continue; + } //L(String.format("State is %d", updating)); switch (state) { case EXPECTING_ACCOUNT: @@ -151,16 +159,45 @@ public class RetrieveTransactionsTask extends Data.accounts.set(accountList); continue; } - m = account_name_re.matcher(line); + m = reAccountName.matcher(line); if (m.find()) { String acct_encoded = m.group(1); String acct_name = URLDecoder.decode(acct_encoded, "UTF-8"); acct_name = acct_name.replace("\"", ""); L(String.format("found account: %s", acct_name)); - addAccount(db, acct_name); - lastAccount = new LedgerAccount(acct_name); - accountList.add(lastAccount); + lastAccount = profile.loadAccount(acct_name); + if (lastAccount == null) { + lastAccount = new LedgerAccount(acct_name); + profile.storeAccount(lastAccount); + } + + // make sure the parent account(s) are present, + // synthesising them if necessary + String parentName = lastAccount.getParentName(); + if (parentName != null) { + Stack toAppend = new Stack<>(); + while (parentName != null) { + if (accountNames.containsKey(parentName)) break; + toAppend.push(parentName); + parentName = new LedgerAccount(parentName) + .getParentName(); + } + while (!toAppend.isEmpty()) { + String aName = toAppend.pop(); + LedgerAccount acc = new LedgerAccount(aName); + acc.setHidden(lastAccount.isHidden()); + if (!onlyStarred || !acc.isHidden()) + accountList.add(acc); + L(String.format("gap-filling with %s", aName)); + accountNames.put(aName, null); + profile.storeAccount(acc); + } + } + + if (!onlyStarred || !lastAccount.isHidden()) + accountList.add(lastAccount); + accountNames.put(acct_name, null); state = ParserState.EXPECTING_ACCOUNT_AMOUNT; L("→ expecting account amount"); @@ -168,7 +205,7 @@ public class RetrieveTransactionsTask extends break; case EXPECTING_ACCOUNT_AMOUNT: - m = account_value_re.matcher(line); + m = reAccountValue.matcher(line); boolean match_found = false; while (m.find()) { throwIfCancelled(); @@ -179,12 +216,8 @@ public class RetrieveTransactionsTask extends if (currency == null) currency = ""; value = value.replace(',', '.'); L("curr=" + currency + ", value=" + value); - db.execSQL( - "insert or replace into account_values(account, currency, value, keep) values(?, ?, ?, 1);", - new Object[]{lastAccount.getName(), - currency, - Float.valueOf(value) - }); + profile.storeAccountValue(lastAccount.getName(), currency, + Float.valueOf(value)); lastAccount.addAmount(Float.parseFloat(value), currency); } @@ -197,7 +230,7 @@ public class RetrieveTransactionsTask extends case EXPECTING_TRANSACTION: if (!line.isEmpty() && (line.charAt(0) == ' ')) continue; - m = transactionStartPattern.matcher(line); + m = reTransactionStart.matcher(line); if (m.find()) { transactionId = Integer.valueOf(m.group(1)); state = ParserState.EXPECTING_TRANSACTION_DESCRIPTION; @@ -212,7 +245,7 @@ public class RetrieveTransactionsTask extends progress.setTotal(transactionId); publishProgress(progress); } - m = endPattern.matcher(line); + m = reEnd.matcher(line); if (m.find()) { L("--- transaction value complete ---"); success = true; @@ -222,7 +255,7 @@ public class RetrieveTransactionsTask extends case EXPECTING_TRANSACTION_DESCRIPTION: if (!line.isEmpty() && (line.charAt(0) == ' ')) continue; - m = transactionDescriptionPattern.matcher(line); + m = reTransactionDescription.matcher(line); if (m.find()) { if (transactionId == 0) throw new TransactionParserException( @@ -243,16 +276,21 @@ public class RetrieveTransactionsTask extends if (line.isEmpty()) { // transaction data collected if (transaction.existsInDb(db)) { - db.execSQL("UPDATE transactions SET keep = 1 WHERE id" + - "=?", new Integer[]{transaction.getId()}); + db.execSQL("UPDATE transactions SET keep = 1 WHERE " + + "profile = ? and id=?", + new Object[]{profile.getUuid(), + transaction.getId() + }); matchedTransactionsCount++; if (matchedTransactionsCount == MATCHING_TRANSACTIONS_LIMIT) { db.execSQL("UPDATE transactions SET keep=1 WHERE " + - "id < ?", - new Integer[]{transaction.getId()}); + "profile = ? and id < ?", + new Object[]{profile.getUuid(), + transaction.getId() + }); success = true; progress.setTotal(progress.getProgress()); publishProgress(progress); @@ -260,12 +298,7 @@ public class RetrieveTransactionsTask extends } } else { - db.execSQL("DELETE from transactions WHERE id=?", - new Integer[]{transaction.getId()}); - db.execSQL("DELETE from transaction_accounts WHERE " + - "transaction_id=?", - new Integer[]{transaction.getId()}); - transaction.insertInto(db); + profile.storeTransaction(transaction); matchedTransactionsCount = 0; progress.setTotal(maxTransactionId); } @@ -274,6 +307,7 @@ public class RetrieveTransactionsTask extends L(String.format( "transaction %s saved → expecting transaction", transaction.getId())); + transaction.finishLoading(); // 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 @@ -284,44 +318,48 @@ public class RetrieveTransactionsTask extends // } } else { - m = transactionDetailsPattern.matcher(line); + m = reTransactionDetails.matcher(line); if (m.find()) { String acc_name = m.group(1); String amount = m.group(2); String currency = m.group(3); + if (currency == null) currency = ""; amount = amount.replace(',', '.'); transaction.addAccount( new LedgerTransactionAccount(acc_name, Float.valueOf(amount), currency)); - L(String.format("%s = %s", acc_name, amount)); + L(String.format("%d: %s = %s", transaction.getId(), + acc_name, amount)); } - else throw new IllegalStateException( - String.format("Can't parse transaction %d details", - transactionId)); + else throw new IllegalStateException(String.format( + "Can't parse transaction %d " + "details: %s", + transactionId, line)); } break; default: throw new RuntimeException( - String.format("Unknown parser updating %s", state.name())); + String.format("Unknown parser updating %s", + state.name())); } } throwIfCancelled(); - db.execSQL("DELETE FROM transactions WHERE keep = 0"); + db.execSQL("DELETE FROM transactions WHERE profile=? AND keep = 0", + new String[]{profile.getUuid()}); db.setTransactionSuccessful(); + + Log.d("db", "Updating transaction value stamp"); + Date now = new Date(); + profile.setLongOption(MLDB.OPT_LAST_SCRAPE, now.getTime()); + Data.lastUpdateDate.set(now); + TransactionListViewModel.scheduleTransactionListReload(); } finally { db.endTransaction(); } } } - - if (success && !isCancelled()) { - Log.d("db", "Updating transaction value stamp"); - MLDB.set_option_value(MLDB.OPT_TRANSACTION_LIST_STAMP, new Date().getTime()); - TransactionListViewModel.scheduleTransactionListReload(ctx); - } } catch (MalformedURLException e) { error = R.string.err_bad_backend_url; @@ -335,6 +373,10 @@ public class RetrieveTransactionsTask extends error = R.string.err_net_io_error; e.printStackTrace(); } + catch (OperationCanceledException e) { + error = R.string.err_cancelled; + e.printStackTrace(); + } finally { Data.backgroundTaskCount.decrementAndGet(); } @@ -343,17 +385,6 @@ public class RetrieveTransactionsTask extends private MainActivity getContext() { return contextRef.get(); } - private void addAccount(SQLiteDatabase db, String name) { - do { - LedgerAccount acc = new LedgerAccount(name); - db.execSQL("update accounts set level = ?, keep = 1 where name = ?", - new Object[]{acc.getLevel(), name}); - db.execSQL("insert into accounts(name, name_upper, parent_name, level) select ?,?," + - "?,? " + "where (select changes() = 0)", - new Object[]{name, name.toUpperCase(), acc.getParentName(), acc.getLevel()}); - name = acc.getParentName(); - } while (name != null); - } private void throwIfCancelled() { if (isCancelled()) throw new OperationCanceledException(null); } @@ -363,17 +394,6 @@ public class RetrieveTransactionsTask extends EXPECTING_TRANSACTION_DESCRIPTION, EXPECTING_TRANSACTION_DETAILS } - public static class Params { - private SharedPreferences backendPref; - - public Params(SharedPreferences backendPref) { - this.backendPref = backendPref; - } - SharedPreferences getBackendPref() { - return backendPref; - } - } - public class Progress { public static final int INDETERMINATE = -1; private int progress;