From 6eff9179f4c804673f778bb058a45f806860656a Mon Sep 17 00:00:00 2001 From: Astatin3 <77305074+Astatin3@users.noreply.github.com> Date: Sat, 14 Sep 2024 16:14:50 -0600 Subject: [PATCH] Add CSV exporting --- README.md | 5 +- app/src/main/AndroidManifest.xml | 9 ++ .../scoutingData/ScoutingDataWriter.java | 4 +- .../ui/transfer/CSVExport.java | 150 ++++++++++++++++++ .../ui/transfer/TransferFragment.java | 44 ++++- .../scoutingapp2025/utility/DataManager.java | 3 +- app/src/main/res/layout/fragment_transfer.xml | 14 +- .../fragment_transfer_file_selector.xml | 24 +-- app/src/main/res/xml/file_paths.xml | 4 + 9 files changed, 230 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/CSVExport.java create mode 100644 app/src/main/res/xml/file_paths.xml diff --git a/README.md b/README.md index 06e947b..a92157b 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ Ridgebotics 2025 scouting app in Android - Make the "Report" menu, A tool that lets users select data to display from the the teams and compare menus. - Make practice mode #### Data Analysis: -- Add CSV exporting of match scouting data. - Statbotics intigration - AI overview of scouting data for a team??? #### Functionality: +- When a field is created, make updated scouting data return null values, not the default value - Make the system for blank and unselected fields better. - Add more types of data fields. - Make server software to allow for easy sync over wifi @@ -21,7 +21,6 @@ Ridgebotics 2025 scouting app in Android #### Data Analysis: - Make the "Compare" menu, cross comparing team's stats. #### Functionality: -- Make pit and match data field builder UIs. I don't want to have to keep editing a variable ## Done: @@ -30,6 +29,7 @@ Ridgebotics 2025 scouting app in Android #### Data Analysis: - Add "history" view type to the teams view menu. - Sentiment analysis of text input type +- Add CSV exporting of match scouting data. #### Functionality: - Improve the code scanning progress indicator. It has a rounding error, I think. - Fix navigation crashes. @@ -37,3 +37,4 @@ Ridgebotics 2025 scouting app in Android - Make the file browser UI - Bluetooth data sync - Formalize error messages & stacktraces +- Make pit and match data field builder UIs. I don't want to have to keep editing a variable diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 734b8d7..0756344 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -40,6 +40,15 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/ScoutingDataWriter.java b/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/ScoutingDataWriter.java index dd60387..8f2ed22 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/ScoutingDataWriter.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/ScoutingDataWriter.java @@ -64,12 +64,12 @@ public class ScoutingDataWriter { case 1: // Int dataTypes[i] = intType.newNull(values[version][i].name); dataTypes[i].forceSetValue(objects.get(i+2).get()); - System.out.println("Loaded INT: " + values[version][i].name + ", ("+ dataTypes[i].get() +")"); + //System.out.println("Loaded INT: " + values[version][i].name + ", ("+ dataTypes[i].get() +")"); break; case 2: // String dataTypes[i] = stringType.newNull(values[version][i].name); dataTypes[i].forceSetValue(objects.get(i+2).get()); - System.out.println("Loaded STR: " + values[version][i].name + ", ("+ dataTypes[i].get() +")"); + //System.out.println("Loaded STR: " + values[version][i].name + ", ("+ dataTypes[i].get() +")"); break; } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/CSVExport.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/CSVExport.java new file mode 100644 index 0000000..99208b7 --- /dev/null +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/CSVExport.java @@ -0,0 +1,150 @@ +package com.astatin3.scoutingapp2025.ui.transfer; + +import static com.astatin3.scoutingapp2025.utility.DataManager.evcode; +import static com.astatin3.scoutingapp2025.utility.DataManager.event; +import static com.astatin3.scoutingapp2025.utility.DataManager.match_latest_values; +import static com.astatin3.scoutingapp2025.utility.DataManager.pit_latest_values; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import androidx.core.content.FileProvider; + +import com.astatin3.scoutingapp2025.scoutingData.ScoutingDataWriter; +import com.astatin3.scoutingapp2025.types.data.dataType; +import com.astatin3.scoutingapp2025.types.frcMatch; +import com.astatin3.scoutingapp2025.types.frcTeam; +import com.astatin3.scoutingapp2025.utility.AlertManager; +import com.astatin3.scoutingapp2025.utility.DataManager; +import com.astatin3.scoutingapp2025.utility.fileEditor; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +public class CSVExport { + private static String[] alliances = {"red", "blue"}; + + + public static void exportMatches(Context c){ + DataManager.reload_event(); + DataManager.reload_match_fields(); + + String data = ""; + + data += ("num,alliance,alliance_position,teamnum,"); + for(int i = 0; i < match_latest_values.length; i++){ + data += (match_latest_values[i].name + ","); + } + data += ("\n"); + + + for(int matchNum = 1; matchNum <= event.matches.size(); matchNum++){ + for(int allianceIndex = 0; allianceIndex <= 1; allianceIndex++){ + String alliance = alliances[allianceIndex]; + for(int alliancePos = 1; alliancePos <= 3; alliancePos++){ + data += (matchNum + ","); + data += (alliance + ","); + data += (alliancePos + ","); + + frcMatch match = event.matches.get(matchNum-1); + int teamNum = 0; + + if(allianceIndex == 0){ + teamNum = match.redAlliance[alliancePos-1]; + }else{ + teamNum = match.blueAlliance[alliancePos-1]; + } + + data += (teamNum + ","); + + String filename = evcode+"-"+matchNum+"-"+alliance+"-"+alliancePos+"-"+teamNum+".matchscoutdata"; + if(!fileEditor.fileExist(filename)){ + data += ("null,".repeat(match_latest_values.length)); + }else{ + ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.match_values, DataManager.match_transferValues); + dataType[] types = psdr.data.array; + for(int i = 0; i < types.length; i++) { + data += (types[i].get() + ","); + } + + } + + data += ("\n"); + }}} + + shareContent(c, evcode+"-matches.csv", data, "text/plain"); + } + + + + public static void exportPits(Context c){ + DataManager.reload_event(); + DataManager.reload_pit_fields(); + + String data = ""; + + data += ("teamnum,teamname,city,teamnum,stateOrProv,school,country,startingYear,"); + for(int i = 0; i < pit_latest_values.length; i++){ + data += (pit_latest_values[i].name + ","); + } + data += ("\n"); + + + for(int teamIndex = 0; teamIndex < event.teams.size(); teamIndex++){ + frcTeam team = event.teams.get(teamIndex); + + data += (team.teamNumber + ","); + data += (team.teamName + ","); + data += (team.city + ","); + data += (team.stateOrProv + ","); + data += (team.school + ","); + data += (team.country + ","); + data += (team.startingYear + ","); + + String filename = evcode+"-"+team.teamNumber+".pitscoutdata"; + if(!fileEditor.fileExist(filename)){ + data += ("null,".repeat(pit_latest_values.length)); + }else{ + ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.pit_values, DataManager.pit_transferValues); + dataType[] types = psdr.data.array; + for(int i = 0; i < types.length; i++) { + data += (types[i].get() + ","); + } + + } + + data += ("\n"); + } + +// System.out.print(data); + + shareContent(c, evcode+"-pits.csv", data, "text/plain"); + } + + + + + + + public static void shareContent(Context context, String fileName, String content, String mimeType) { + try { + File file = new File(context.getCacheDir(), fileName); + FileOutputStream fos = new FileOutputStream(file); + fos.write(content.getBytes()); + fos.close(); + + Uri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file); + + Intent shareIntent = new Intent(Intent.ACTION_SEND); + shareIntent.setType(mimeType); + shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri); + shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + context.startActivity(Intent.createChooser(shareIntent, "Share using")); + } catch (IOException e) { + AlertManager.error(e); + } + } +} diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TransferFragment.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TransferFragment.java index ad128bb..f22a947 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TransferFragment.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TransferFragment.java @@ -3,6 +3,7 @@ package com.astatin3.scoutingapp2025.ui.transfer; import static androidx.navigation.fragment.FragmentKt.findNavController; import android.app.AlertDialog; +import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -21,14 +22,12 @@ import com.astatin3.scoutingapp2025.ui.transfer.codes.CodeGeneratorView; public class TransferFragment extends Fragment { private FragmentTransferBinding binding; - private boolean submenu = false; - - private enum TransferTypes { - CAMERA, - BLUETOOTH, - LOCAL_WIFI, - SCOUTING_SERVER - } +// private enum TransferTypes { +// CAMERA, +// BLUETOOTH, +// LOCAL_WIFI, +// SCOUTING_SERVER +// } String evcode; @@ -68,6 +67,7 @@ public class TransferFragment extends Fragment { if(evcode.equals("unset")){ binding.noEventError.setVisibility(View.VISIBLE); binding.uploadButton.setVisibility(View.GONE); + binding.CSVButton.setVisibility(View.GONE); binding.downloadButton.setVisibility(View.VISIBLE); return binding.getRoot(); } @@ -76,6 +76,34 @@ public class TransferFragment extends Fragment { start_upload(); }); + binding.CSVButton.setOnClickListener(v -> { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle("Chose data"); + + builder.setNegativeButton("Pit data", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + CSVExport.exportPits(getContext()); + } + }); + + builder.setPositiveButton("Match data", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + CSVExport.exportMatches(getContext()); + } + }); + + builder.setNeutralButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + builder.show(); + }); + if(!latestSettings.settings.get_wifi_mode()) binding.TBAButton.setVisibility(View.GONE); diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/utility/DataManager.java b/app/src/main/java/com/astatin3/scoutingapp2025/utility/DataManager.java index f48df00..7899969 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/utility/DataManager.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/utility/DataManager.java @@ -10,7 +10,8 @@ public class DataManager { public static String evcode; public static frcEvent event; public static void reload_event(){ - evcode = getevcode(); event = frcEvent.decode(fileEditor.readFile(evcode + ".eventdata")); + evcode = getevcode(); + event = frcEvent.decode(fileEditor.readFile(evcode + ".eventdata")); } public static String getevcode() { diff --git a/app/src/main/res/layout/fragment_transfer.xml b/app/src/main/res/layout/fragment_transfer.xml index 355130c..dd984f2 100644 --- a/app/src/main/res/layout/fragment_transfer.xml +++ b/app/src/main/res/layout/fragment_transfer.xml @@ -25,8 +25,7 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:visibility="gone"> + app:layout_constraintTop_toTopOf="parent">