Add CSV exporting

This commit is contained in:
Astatin3
2024-09-14 16:14:50 -06:00
parent ccf0b56636
commit 6eff9179f4
9 changed files with 230 additions and 27 deletions
+3 -2
View File
@@ -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 the "Report" menu, A tool that lets users select data to display from the the teams and compare menus.
- Make practice mode - Make practice mode
#### Data Analysis: #### Data Analysis:
- Add CSV exporting of match scouting data.
- Statbotics intigration - Statbotics intigration
- AI overview of scouting data for a team??? - AI overview of scouting data for a team???
#### Functionality: #### 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. - Make the system for blank and unselected fields better.
- Add more types of data fields. - Add more types of data fields.
- Make server software to allow for easy sync over wifi - Make server software to allow for easy sync over wifi
@@ -21,7 +21,6 @@ Ridgebotics 2025 scouting app in Android
#### Data Analysis: #### Data Analysis:
- Make the "Compare" menu, cross comparing team's stats. - Make the "Compare" menu, cross comparing team's stats.
#### Functionality: #### Functionality:
- Make pit and match data field builder UIs. I don't want to have to keep editing a variable
## Done: ## Done:
@@ -30,6 +29,7 @@ Ridgebotics 2025 scouting app in Android
#### Data Analysis: #### Data Analysis:
- Add "history" view type to the teams view menu. - Add "history" view type to the teams view menu.
- Sentiment analysis of text input type - Sentiment analysis of text input type
- Add CSV exporting of match scouting data.
#### Functionality: #### Functionality:
- Improve the code scanning progress indicator. It has a rounding error, I think. - Improve the code scanning progress indicator. It has a rounding error, I think.
- Fix navigation crashes. - Fix navigation crashes.
@@ -37,3 +37,4 @@ Ridgebotics 2025 scouting app in Android
- Make the file browser UI - Make the file browser UI
- Bluetooth data sync - Bluetooth data sync
- Formalize error messages & stacktraces - Formalize error messages & stacktraces
- Make pit and match data field builder UIs. I don't want to have to keep editing a variable
+9
View File
@@ -40,6 +40,15 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application> </application>
</manifest> </manifest>
@@ -64,12 +64,12 @@ public class ScoutingDataWriter {
case 1: // Int case 1: // Int
dataTypes[i] = intType.newNull(values[version][i].name); dataTypes[i] = intType.newNull(values[version][i].name);
dataTypes[i].forceSetValue(objects.get(i+2).get()); 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; break;
case 2: // String case 2: // String
dataTypes[i] = stringType.newNull(values[version][i].name); dataTypes[i] = stringType.newNull(values[version][i].name);
dataTypes[i].forceSetValue(objects.get(i+2).get()); 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; break;
} }
} }
@@ -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);
}
}
}
@@ -3,6 +3,7 @@ package com.astatin3.scoutingapp2025.ui.transfer;
import static androidx.navigation.fragment.FragmentKt.findNavController; import static androidx.navigation.fragment.FragmentKt.findNavController;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -21,14 +22,12 @@ import com.astatin3.scoutingapp2025.ui.transfer.codes.CodeGeneratorView;
public class TransferFragment extends Fragment { public class TransferFragment extends Fragment {
private FragmentTransferBinding binding; private FragmentTransferBinding binding;
private boolean submenu = false; // private enum TransferTypes {
// CAMERA,
private enum TransferTypes { // BLUETOOTH,
CAMERA, // LOCAL_WIFI,
BLUETOOTH, // SCOUTING_SERVER
LOCAL_WIFI, // }
SCOUTING_SERVER
}
String evcode; String evcode;
@@ -68,6 +67,7 @@ public class TransferFragment extends Fragment {
if(evcode.equals("unset")){ if(evcode.equals("unset")){
binding.noEventError.setVisibility(View.VISIBLE); binding.noEventError.setVisibility(View.VISIBLE);
binding.uploadButton.setVisibility(View.GONE); binding.uploadButton.setVisibility(View.GONE);
binding.CSVButton.setVisibility(View.GONE);
binding.downloadButton.setVisibility(View.VISIBLE); binding.downloadButton.setVisibility(View.VISIBLE);
return binding.getRoot(); return binding.getRoot();
} }
@@ -76,6 +76,34 @@ public class TransferFragment extends Fragment {
start_upload(); 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()) if(!latestSettings.settings.get_wifi_mode())
binding.TBAButton.setVisibility(View.GONE); binding.TBAButton.setVisibility(View.GONE);
@@ -10,7 +10,8 @@ public class DataManager {
public static String evcode; public static String evcode;
public static frcEvent event; public static frcEvent event;
public static void reload_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() { public static String getevcode() {
+12 -2
View File
@@ -25,8 +25,7 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent">
tools:visibility="gone">
<Button <Button
android:id="@+id/uploadButton" android:id="@+id/uploadButton"
@@ -39,6 +38,17 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/CSVButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CSV"
android:textSize="34sp"
app:layout_constraintBottom_toTopOf="@+id/downloadButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/uploadButton" />
<Button <Button
android:id="@+id/downloadButton" android:id="@+id/downloadButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -13,18 +13,6 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/file_selector_searchbar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#D33D3D3D"
android:ems="10"
android:inputType="text"
android:textColor="#FFFFFF"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@@ -47,4 +35,16 @@
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<EditText
android:id="@+id/file_selector_searchbar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#D33D3D3D"
android:ems="10"
android:inputType="text"
android:textColor="#FFFFFF"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="shared_files" path="/" />
</paths>