package com.github.andlyticsproject.io;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.CSVWriter;
import com.github.andlyticsproject.model.AppStats;
import com.github.andlyticsproject.model.Revenue;
import com.github.andlyticsproject.util.FileUtils;
import com.github.andlyticsproject.util.Utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
@SuppressLint("SimpleDateFormat")
public class StatsCsvReaderWriter {
private static final String TAG = StatsCsvReaderWriter.class.getSimpleName();
public static final String[] HEADER_LIST = new String[] { "PACKAGE_NAME", "DATE",
"TOTAL_DOWNLOADS", "ACTIVE_INSTALLS", "NUMBER_OF_COMMENTS", "1_STAR_RATINGS",
"2_STAR_RATINGS", "3_STAR_RATINGS", "4_STAR_RATINGS", "5_STAR_RATINGS", "VERSION_CODE",
"NUM_ERRORS", "TOTAL_REVENUE", "CURRENCY" };
private static final String EXPORT_DIR = "andlytics/";
private static final String DEFAULT_EXPORT_ZIP_FILE = "andlytics.zip";
private static final String EXPORT_ZIP_FILE_TEMPLATE = "andlytics-%s.zip";
private static final String CSV_SUFFIX = ".csv";
// create this every time because SDF is not threadsafe
private static SimpleDateFormat createTimestampFormat() {
SimpleDateFormat result = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
result.setTimeZone(TimeZone.getTimeZone("UTC"));
return result;
}
public static String getExportDirPath() {
return getExportDir().getAbsolutePath();
}
public static File getExportDir() {
return new File(Environment.getExternalStorageDirectory(), EXPORT_DIR);
}
public static File getDefaultExportFile() {
return new File(getExportDir(), DEFAULT_EXPORT_ZIP_FILE);
}
public static File getExportFileForAccount(String accountName) {
return new File(getExportDir(), String.format(EXPORT_ZIP_FILE_TEMPLATE, accountName));
}
public static String getAccountNameForExport(String filename) {
int firstDashIdx = filename.indexOf('-');
int suffixIdx = filename.indexOf(".zip");
if (firstDashIdx == -1 || suffixIdx == -1) {
return null;
}
return filename.substring(firstDashIdx + 1, suffixIdx);
}
public StatsCsvReaderWriter(Context context) {
}
@SuppressWarnings("resource")
public void writeStats(String packageName, List<AppStats> stats, ZipOutputStream zip)
throws IOException {
zip.putNextEntry(new ZipEntry(packageName + CSV_SUFFIX));
// we don't own the stream, it's closed by the caller
CSVWriter writer = new CSVWriter(new OutputStreamWriter(zip));
writer.writeNext(HEADER_LIST);
String[] line = new String[HEADER_LIST.length];
for (AppStats stat : stats) {
line[0] = packageName;
line[1] = createTimestampFormat().format(stat.getDate());
line[2] = Integer.toString(stat.getTotalDownloads());
line[3] = Integer.toString(stat.getActiveInstalls());
line[4] = Integer.toString(stat.getNumberOfComments());
line[5] = Utils.safeToString(stat.getRating1());
line[6] = Utils.safeToString(stat.getRating2());
line[7] = Utils.safeToString(stat.getRating3());
line[8] = Utils.safeToString(stat.getRating4());
line[9] = Utils.safeToString(stat.getRating5());
line[10] = Utils.safeToString(stat.getVersionCode());
line[11] = Utils.safeToString(stat.getNumberOfErrors());
line[12] = stat.getTotalRevenue() == null ? "" : String.format(Locale.US, "%.2f", stat
.getTotalRevenue().getAmount());
line[13] = stat.getTotalRevenue() == null ? "" : stat.getTotalRevenue()
.getCurrencyCode();
writer.writeNext(line);
}
writer.flush();
}
public static List<String> getImportFileNamesFromZip(String accountName,
List<String> packageNames, String zipFilename) throws ServiceException {
List<String> result = new ArrayList<String>();
try {
if (!new File(zipFilename).exists()) {
return result;
}
ZipFile zipFile = new ZipFile(zipFilename);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
InputStream in = zipFile.getInputStream(entry);
if (isValidFile(accountName, in, packageNames)) {
result.add(entry.getName());
}
}
zipFile.close();
return result;
} catch (IOException e) {
Log.e(TAG, "Error reading zip file: " + e.getMessage());
return new ArrayList<String>();
}
}
private static boolean isValidFile(String accountName, InputStream in, List<String> packageNames)
throws ServiceException {
if (packageNames.isEmpty()) {
return true;
}
CSVReader reader = null;
try {
reader = new CSVReader(new InputStreamReader(in));
String[] firstLine = reader.readNext();
if (firstLine != null) {
if (HEADER_LIST.length >= firstLine.length) {
for (int i = 0; i < firstLine.length - 1; i++) {
if (!HEADER_LIST[i].equals(firstLine[i])) {
return false;
}
}
// validate package name
String[] secondLine = reader.readNext();
String packageName = secondLine[0];
if (secondLine != null) {
return packageNames.contains(packageName);
}
}
}
} catch (FileNotFoundException e) {
throw new ServiceException(e);
} catch (IOException e) {
throw new ServiceException(e);
} finally {
FileUtils.closeSilently(reader);
}
return false;
}
public static String getPackageName(String filename) {
int suffixIdx = filename.indexOf(CSV_SUFFIX);
if (suffixIdx == -1) {
return null;
}
return filename.substring(0, suffixIdx);
}
@SuppressWarnings("resource")
public List<AppStats> readStats(InputStream in) throws ServiceException {
List<AppStats> appStats = new ArrayList<AppStats>();
CSVReader reader;
try {
// we don't own the stream, it's closed by the caller
reader = new CSVReader(new InputStreamReader(in));
String[] firstLine = reader.readNext();
if (firstLine != null) {
String[] nextLine = null;
while ((nextLine = reader.readNext()) != null) {
AppStats stats = new AppStats();
stats.setPackageName(nextLine[0]);
stats.setDate(createTimestampFormat().parse(nextLine[1]));
stats.setTotalDownloads(Integer.parseInt(nextLine[2]));
stats.setActiveInstalls(Integer.parseInt(nextLine[3]));
stats.setNumberOfComments(Integer.parseInt(nextLine[4]));
stats.setRating1(Integer.parseInt(nextLine[5]));
stats.setRating2(Integer.parseInt(nextLine[6]));
stats.setRating3(Integer.parseInt(nextLine[7]));
stats.setRating4(Integer.parseInt(nextLine[8]));
stats.setRating5(Integer.parseInt(nextLine[9]));
if (nextLine.length > 10) {
stats.setVersionCode(Integer.parseInt(nextLine[10]));
}
if (nextLine.length > 11) {
String numErrorsStr = nextLine[11];
stats.setNumberOfErrors(parseInt(numErrorsStr));
}
if (nextLine.length > 12) {
String totalRevenueStr = nextLine[12];
if (!TextUtils.isEmpty(totalRevenueStr)) {
String currency = nextLine[13];
stats.setTotalRevenue(new Revenue(Revenue.Type.TOTAL,
parseDouble(totalRevenueStr.trim()), currency));
}
}
appStats.add(stats);
}
}
} catch (FileNotFoundException e) {
throw new ServiceException(e);
} catch (IOException e) {
throw new ServiceException(e);
} catch (ParseException e) {
throw new ServiceException(e);
}
return appStats;
}
private Double parseDouble(String totalRevenueStr) {
return TextUtils.isEmpty(totalRevenueStr) ? null : Double.parseDouble(totalRevenueStr);
}
private Integer parseInt(String intStr) {
return TextUtils.isEmpty(intStr) ? null : Integer.parseInt(intStr);
}
public String readPackageName(String fileName) throws ServiceException {
try {
return readPackageName(new FileInputStream(new File(getExportDirPath(), fileName)));
} catch (IOException e) {
throw new ServiceException(e);
}
}
public String readPackageName(InputStream in) throws ServiceException {
String packageName = null;
CSVReader reader;
try {
reader = new CSVReader(new InputStreamReader(in));
String[] firstLine = reader.readNext();
if (firstLine != null) {
String[] nextLine = null;
while ((nextLine = reader.readNext()) != null) {
packageName = nextLine[0];
}
}
reader.close();
} catch (FileNotFoundException e) {
throw new ServiceException(e);
} catch (IOException e) {
throw new ServiceException(e);
}
return packageName;
}
}