186 lines
6.7 KiB
D
186 lines
6.7 KiB
D
module util.sample_data;
|
|
|
|
import slf4d;
|
|
import handy_http_primitives : Optional;
|
|
|
|
import auth;
|
|
import profile;
|
|
import account;
|
|
import transaction;
|
|
import transaction.dto;
|
|
import util.money;
|
|
import util.data;
|
|
|
|
import std.random;
|
|
import std.conv;
|
|
import std.array;
|
|
import std.datetime;
|
|
import std.typecons;
|
|
|
|
void generateSampleData() {
|
|
UserRepository userRepo = new FileSystemUserRepository;
|
|
// Remove all existing user data.
|
|
foreach (User user; userRepo.findAll()) {
|
|
userRepo.deleteByUsername(user.username);
|
|
}
|
|
|
|
const int userCount = uniform(5, 10);
|
|
for (int i = 0; i < userCount; i++) {
|
|
try {
|
|
generateRandomUser(i, userRepo);
|
|
} catch (Throwable t) {
|
|
import std.stdio;
|
|
stderr.writeln(t);
|
|
throw t;
|
|
}
|
|
}
|
|
info("Random sample data generation complete.");
|
|
}
|
|
|
|
void generateRandomUser(int idx, UserRepository userRepo) {
|
|
string username = "testuser" ~ idx.to!string;
|
|
string password = "testpass";
|
|
infoF!"Generating random user %s, password: %s."(username, password);
|
|
User user = createNewUser(userRepo, username, password);
|
|
ProfileRepository profileRepo = new FileSystemProfileRepository(username);
|
|
const int profileCount = uniform(1, 5);
|
|
for (int i = 0; i < profileCount; i++) {
|
|
generateRandomProfile(i, profileRepo);
|
|
}
|
|
}
|
|
|
|
void generateRandomProfile(int idx, ProfileRepository profileRepo) {
|
|
string profileName = "test-profile-" ~ idx.to!string;
|
|
infoF!" Generating random profile %s."(profileName);
|
|
Profile profile = profileRepo.createProfile(profileName);
|
|
ProfileDataSource ds = profileRepo.getDataSource(profile);
|
|
|
|
ds.getPropertiesRepository().setProperty("sample-data-idx", idx.to!string);
|
|
Currency preferredCurrency = choice(ALL_CURRENCIES);
|
|
|
|
const int accountCount = uniform(3, 10);
|
|
for (int i = 0; i < accountCount; i++) {
|
|
generateRandomAccount(i, ds, preferredCurrency);
|
|
}
|
|
|
|
auto vendorRepo = ds.getTransactionVendorRepository();
|
|
const int vendorCount = uniform(5, 30);
|
|
for (int i = 0; i < vendorCount; i++) {
|
|
vendorRepo.insert("Test Vendor " ~ to!string(i), "Testing vendor for sample data.");
|
|
}
|
|
infoF!" Generated %d random vendors."(vendorCount);
|
|
|
|
auto categoryRepo = ds.getTransactionCategoryRepository();
|
|
const int categoryCount = uniform(5, 30);
|
|
for (int i = 0; i < categoryCount; i++) {
|
|
categoryRepo.insert(Optional!ulong.empty, "Test Category " ~ to!string(i), "Testing category.", "FFFFFF");
|
|
}
|
|
infoF!" Generated %d random categories."(categoryCount);
|
|
|
|
generateRandomTransactions(ds);
|
|
}
|
|
|
|
void generateRandomAccount(int idx, ProfileDataSource ds, Currency preferredCurrency) {
|
|
AccountRepository accountRepo = ds.getAccountRepository();
|
|
string idxStr = idx.to!string;
|
|
string numberSuffix = "0".replicate(4 - idxStr.length) ~ idxStr;
|
|
string name = "Test Account " ~ idxStr;
|
|
AccountType type = choice(ALL_ACCOUNT_TYPES);
|
|
Currency currency = preferredCurrency;
|
|
if (uniform01() < 0.1) {
|
|
currency = choice(ALL_CURRENCIES);
|
|
}
|
|
string description = "This is a testing account generated by util.sample_data.generateRandomAccount().";
|
|
Account account = accountRepo.insert(
|
|
type,
|
|
numberSuffix,
|
|
name,
|
|
currency,
|
|
description
|
|
);
|
|
infoF!" Generated random account: %s, #%s, %s"(name, numberSuffix, currency.code);
|
|
}
|
|
|
|
void generateRandomTransactions(ProfileDataSource ds) {
|
|
const TransactionVendor[] vendors = ds.getTransactionVendorRepository.findAll();
|
|
const TransactionCategory[] categories = ds.getTransactionCategoryRepository()
|
|
.findAllByParentId(Optional!ulong.empty);
|
|
const Account[] accounts = ds.getAccountRepository().findAll();
|
|
|
|
SysTime timestamp = Clock.currTime(UTC()) - seconds(1);
|
|
|
|
for (int i = 0; i < 100; i++) {
|
|
AddTransactionPayload data;
|
|
data.timestamp = timestamp.toISOExtString();
|
|
if (uniform01() < 0.7) {
|
|
data.vendorId = Optional!ulong.of(choice(vendors).id).toNullable;
|
|
}
|
|
if (uniform01() < 0.8) {
|
|
data.categoryId = Optional!ulong.of(choice(categories).id).toNullable;
|
|
}
|
|
|
|
// Randomly choose an account to credit / debit the transaction to.
|
|
Account primaryAccount = choice(accounts);
|
|
data.currencyCode = primaryAccount.currency.code;
|
|
Optional!ulong secondaryAccountId;
|
|
if (uniform01() < 0.25) {
|
|
foreach (acc; accounts) {
|
|
if (acc.id != primaryAccount.id && acc.currency == primaryAccount.currency) {
|
|
secondaryAccountId = Optional!ulong.of(acc.id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (uniform01() < 0.5) {
|
|
data.creditedAccountId = Optional!ulong.of(primaryAccount.id).toNullable;
|
|
if (secondaryAccountId) data.debitedAccountId = secondaryAccountId.toNullable;
|
|
} else {
|
|
data.debitedAccountId = Optional!ulong.of(primaryAccount.id).toNullable;
|
|
if (secondaryAccountId) data.creditedAccountId = secondaryAccountId.toNullable;
|
|
}
|
|
|
|
// Randomly choose some tags to add.
|
|
string[] tags;
|
|
foreach (n; 1..10) {
|
|
if (uniform01 < 0.25) {
|
|
tags ~= "tag-" ~ n.to!string;
|
|
}
|
|
}
|
|
data.tags = tags;
|
|
|
|
data.amount = uniform(0, 1_000_000);
|
|
data.description = "This is a sample transaction which was generated as part of sample data.";
|
|
|
|
// Generate random line items:
|
|
if (uniform01 < 0.5) {
|
|
long lineItemTotal = 0;
|
|
foreach (n; 1..uniform(1, 20)) {
|
|
AddTransactionPayload.LineItem item;
|
|
item.valuePerItem = uniform(1, 10_000);
|
|
item.quantity = uniform(1, 5);
|
|
lineItemTotal += item.quantity * item.valuePerItem;
|
|
item.description = "Sample item " ~ n.to!string;
|
|
if (uniform01 < 0.5) {
|
|
TransactionCategory category = choice(categories);
|
|
item.categoryId = category.id;
|
|
}
|
|
data.lineItems ~= item;
|
|
}
|
|
long diff = data.amount - lineItemTotal;
|
|
// Add one final line item that adds up to the transaction total.
|
|
if (diff != 0) {
|
|
data.lineItems ~= AddTransactionPayload.LineItem(
|
|
diff,
|
|
1,
|
|
"Last item which reconciles line items total with transaction amount.",
|
|
Nullable!ulong.init
|
|
);
|
|
}
|
|
}
|
|
|
|
auto txn = addTransaction(ds, data);
|
|
infoF!" Generated transaction %d"(txn.id);
|
|
timestamp -= seconds(uniform(10, 1_000_000));
|
|
}
|
|
}
|