From 0217efd7e32c62503dc006f8fdbd7a843cf22986 Mon Sep 17 00:00:00 2001 From: Aztechs157-Git Date: Tue, 21 Jul 2026 20:32:26 -0400 Subject: [PATCH 1/6] Initialize Data Dashboard and fix scoring logic for 2026 REBUILT --- .idea/AndroidProjectSystem.xml | 6 + .idea/deploymentTargetSelector.xml | 1 + .idea/planningMode.xml | 10 + app/src/main/AndroidManifest.xml | 11 ++ .../ActivityCompetitionSelection.java | 6 + .../roboticsscoutingmatchapp/MatchData.java | 93 +++++++++ .../MatchHistoryAdapter.java | 59 ++++++ .../example/roboticsscoutingmatchapp/U.java | 2 +- .../activityAfterMatch.java | 2 +- .../activityDataShowing.java | 125 ++++++++---- .../layout/activity_competition_selection.xml | 8 +- .../main/res/layout/activity_data_showing.xml | 186 +++++++++++++----- .../main/res/layout/item_match_history.xml | 58 ++++++ app/src/main/res/values/strings.xml | 1 + 14 files changed, 480 insertions(+), 88 deletions(-) create mode 100644 .idea/AndroidProjectSystem.xml create mode 100644 .idea/planningMode.xml create mode 100644 app/src/main/java/com/example/roboticsscoutingmatchapp/MatchData.java create mode 100644 app/src/main/java/com/example/roboticsscoutingmatchapp/MatchHistoryAdapter.java create mode 100644 app/src/main/res/layout/item_match_history.xml diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b268ef3..ca16a99 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,6 +4,7 @@ diff --git a/.idea/planningMode.xml b/.idea/planningMode.xml new file mode 100644 index 0000000..05fc08a --- /dev/null +++ b/.idea/planningMode.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9631586..7f7c15f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/ActivityCompetitionSelection.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/ActivityCompetitionSelection.java index df07a7c..66e8cad 100644 --- a/app/src/main/java/com/example/roboticsscoutingmatchapp/ActivityCompetitionSelection.java +++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/ActivityCompetitionSelection.java @@ -45,6 +45,7 @@ protected void onCreate(Bundle savedInstanceState) { // RadioButton worldsButton = findViewById(R.id.comp_worlds); // RadioButton testButton = findViewById(R.id.comp_test); Button saveButton = findViewById(R.id.save_button); + Button viewDashboardButton = findViewById(R.id.view_dashboard_button); Toast unfilledMessage = new Toast(this); unfilledMessage.setDuration(Toast.LENGTH_SHORT); @@ -116,5 +117,10 @@ protected void onCreate(Bundle savedInstanceState) { unfilledMessage.show(); } }); + + viewDashboardButton.setOnClickListener((l) -> { + Intent i = new Intent(this, activityDataShowing.class); + this.startActivity(i); + }); } } \ No newline at end of file diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchData.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchData.java new file mode 100644 index 0000000..5f39994 --- /dev/null +++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchData.java @@ -0,0 +1,93 @@ +package com.example.roboticsscoutingmatchapp; + +public class MatchData { + public String competition; + public int dataVersion; + public String scout; + public String teamNum; + public String teamColor; + public String matchNum; + public boolean preloadedFuel; + public String startingPos; + public int autoScored; + public String autoAccuracy; + public int autoPassed; + public boolean autoHang; + public int teleopScored; + public String teleopAccuracy; + public int teleopPassed; + public String hangStatus; + public int hangTime; + public String accuracyPos; + public boolean overBump; + public boolean underTrench; + public boolean playedDefense; + public boolean collectedFuel; + public boolean passedFuel; + public boolean didNothing; + public boolean other; + public String defense; + public String stopReason; + public String rank; + public String comments; + + public MatchData(String[] csvRow) { + if (csvRow.length < 29) return; + + this.competition = csvRow[0]; + this.dataVersion = tryParseInt(csvRow[1]); + this.scout = csvRow[2]; + this.teamNum = csvRow[3]; + this.teamColor = csvRow[4]; + this.matchNum = csvRow[5]; + this.preloadedFuel = Boolean.parseBoolean(csvRow[6]); + this.startingPos = csvRow[7]; + this.autoScored = tryParseInt(csvRow[8]); + this.autoAccuracy = csvRow[9]; + this.autoPassed = tryParseInt(csvRow[10]); + this.autoHang = Boolean.parseBoolean(csvRow[11]); + this.teleopScored = tryParseInt(csvRow[12]); + this.teleopAccuracy = csvRow[13]; + this.teleopPassed = tryParseInt(csvRow[14]); + this.hangStatus = csvRow[15]; + this.hangTime = tryParseInt(csvRow[16]); + this.accuracyPos = csvRow[17]; + this.overBump = Boolean.parseBoolean(csvRow[18]); + this.underTrench = Boolean.parseBoolean(csvRow[19]); + this.playedDefense = Boolean.parseBoolean(csvRow[20]); + this.collectedFuel = Boolean.parseBoolean(csvRow[21]); + this.passedFuel = Boolean.parseBoolean(csvRow[22]); + this.didNothing = Boolean.parseBoolean(csvRow[23]); + this.other = Boolean.parseBoolean(csvRow[24]); + this.defense = csvRow[25]; + this.stopReason = csvRow[26]; + this.rank = csvRow[27]; + this.comments = csvRow[28]; + } + + public double getCalculatedAutoScore() { + return autoScored * getAccuracyMultiplier(autoAccuracy); + } + + public double getCalculatedTeleopScore() { + return teleopScored * getAccuracyMultiplier(teleopAccuracy); + } + + private double getAccuracyMultiplier(String accuracy) { + if (accuracy == null) return 0; + if (accuracy.contains("10%")) return 0.05; + if (accuracy.contains("25%")) return 0.25; + if (accuracy.contains("50%")) return 0.50; + if (accuracy.contains("75%")) return 0.75; + if (accuracy.contains("95%")) return 0.97; + return 0; + } + + private int tryParseInt(String val) { + try { + return Integer.parseInt(val.trim()); + } catch (Exception e) { + return 0; + } + } +} diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchHistoryAdapter.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchHistoryAdapter.java new file mode 100644 index 0000000..2e5db11 --- /dev/null +++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchHistoryAdapter.java @@ -0,0 +1,59 @@ +package com.example.roboticsscoutingmatchapp; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +public class MatchHistoryAdapter extends RecyclerView.Adapter { + + private List matches; + + public MatchHistoryAdapter(List matches) { + this.matches = matches; + } + + public void updateData(List newMatches) { + this.matches = newMatches; + notifyDataSetChanged(); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_match_history, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + MatchData match = matches.get(position); + holder.matchNum.setText("M #" + match.matchNum); + holder.scoreSummary.setText(String.format("Auto: %d | Tele: %d", match.autoScored, match.teleopScored)); + holder.hangStatus.setText("Climb: " + match.hangStatus); + holder.scouterName.setText(match.scout); + } + + @Override + public int getItemCount() { + return matches.size(); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + TextView matchNum, scoreSummary, hangStatus, scouterName; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + matchNum = itemView.findViewById(R.id.match_num_text); + scoreSummary = itemView.findViewById(R.id.score_summary_text); + hangStatus = itemView.findViewById(R.id.hang_status_text); + scouterName = itemView.findViewById(R.id.scout_name_text); + } + } +} diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/U.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/U.java index 5cfb3e1..b52fc04 100644 --- a/app/src/main/java/com/example/roboticsscoutingmatchapp/U.java +++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/U.java @@ -21,7 +21,7 @@ public class U extends AppCompatActivity{ - public final int DATA_VERSION = 1; + public final int DATA_VERSION = 2; // Characters to strip in input fields for stripText method public static final String [] DELIMITER = {",", ";", ":", "|", "\n"}; public static final String [] WHITESPACE = {" "}; diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAfterMatch.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAfterMatch.java index 2f1d30f..dfbfcd5 100644 --- a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAfterMatch.java +++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAfterMatch.java @@ -153,8 +153,8 @@ protected void onCreate(Bundle savedInstanceState) { // Because it's the back button, these values can have no value String afterMatchInfo = ""; - afterMatchInfo += u.getData(underTrench) + ","; afterMatchInfo += u.getData(overBump) + ","; + afterMatchInfo += u.getData(underTrench) + ","; afterMatchInfo += u.getData(playedDefense) + ","; afterMatchInfo += u.getData(collectedFuel) + ","; afterMatchInfo += u.getData(passedFuel) + ","; diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityDataShowing.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityDataShowing.java index dd87fd3..e2c637c 100644 --- a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityDataShowing.java +++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityDataShowing.java @@ -3,20 +3,33 @@ import android.os.Bundle; import android.os.Environment; import android.util.Log; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.TextView; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class activityDataShowing extends AppCompatActivity { + private List allMatches = new ArrayList<>(); + private Set teamNumbers = new HashSet<>(); + private MatchHistoryAdapter adapter; + private TextView avgAutoText, avgTeleopText, climbRateText; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -28,42 +41,86 @@ protected void onCreate(Bundle savedInstanceState) { return insets; }); - final String BASE = "match_scouting_2025"; - String base = BASE + ".csv"; - String comp = BASE + "_comp.csv"; - String radar = BASE + "_radar.csv"; - String aggregated = BASE + "_agg.csv"; - String delimiter = ","; - - ArrayList aggArr = new ArrayList<>(); - - - boolean baseE = false; - boolean compE = false; - boolean radarE = false; - boolean aggE = false; - - try{ - File baseF = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), base); - File compF = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), comp); - File radarF = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), radar); - File aggF = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), aggregated); - - if(baseF.exists()){baseE = true;} - if(compF.exists()){compE = true;} - if(radarF.exists()){radarE = true;} - if(aggF.exists()){aggE = true;} - - if(aggE) { - BufferedReader brAgg = new BufferedReader(new FileReader(aggF)); - String line; - while((line = brAgg.readLine()) != null){ - aggArr.add(line.split(delimiter)); + avgAutoText = findViewById(R.id.avg_auto_scored); + avgTeleopText = findViewById(R.id.avg_teleop_scored); + climbRateText = findViewById(R.id.climb_success_rate); + + loadLocalData(); + setupDashboard(); + } + + private void loadLocalData() { + File docDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS); + File[] files = docDir.listFiles((dir, name) -> name.startsWith("match_scouting_") && name.endsWith(".csv")); + + if (files == null) return; + + for (File file : files) { + try (BufferedReader br = new BufferedReader(new FileReader(file))) { + String line = br.readLine(); + if (line != null) { + // Note: In your current app, you write multiple sections without newlines sometimes? + // Actually, bw.write(preMatchSaveString); bw.write(autoSaveString); ... + // If they don't have commas or newlines, parsing will be hard. + // But AfterMatchActivity writes bw.write(preMatchSaveString); etc. + // Let's assume it's one big comma-separated line as per your notebook logic. + String[] data = line.split(",", -1); + MatchData match = new MatchData(data); + if (match.teamNum != null && !match.teamNum.isEmpty()) { + allMatches.add(match); + teamNumbers.add(match.teamNum); + } + } + } catch (Exception e) { + Log.e("DataShowing", "Error loading file: " + file.getName(), e); + } + } + } + + private void setupDashboard() { + AutoCompleteTextView searchView = findViewById(R.id.team_search_view); + ArrayAdapter teamAdapter = new ArrayAdapter<>(this, + android.R.layout.simple_dropdown_item_1line, new ArrayList<>(teamNumbers)); + searchView.setAdapter(teamAdapter); + + RecyclerView recyclerView = findViewById(R.id.match_history_recycler); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + adapter = new MatchHistoryAdapter(new ArrayList<>()); + recyclerView.setAdapter(adapter); + + searchView.setOnItemClickListener((parent, view, position, id) -> { + String selectedTeam = (String) parent.getItemAtPosition(position); + updateDashboard(selectedTeam); + }); + } + + private void updateDashboard(String teamNum) { + List teamMatches = new ArrayList<>(); + double totalAuto = 0; + double totalTeleop = 0; + int climbs = 0; + + for (MatchData m : allMatches) { + if (m.teamNum.equals(teamNum)) { + teamMatches.add(m); + totalAuto += m.getCalculatedAutoScore(); + totalTeleop += m.getCalculatedTeleopScore(); + if (m.hangStatus != null && !m.hangStatus.equalsIgnoreCase("None")) { + climbs++; } } - } catch (Exception e) { - Log.e("e", "onCreate: ", e); } + if (teamMatches.isEmpty()) return; + + double avgAuto = (double) totalAuto / teamMatches.size(); + double avgTele = (double) totalTeleop / teamMatches.size(); + double climbRate = (double) climbs / teamMatches.size() * 100; + + avgAutoText.setText(String.format("%.2f", avgAuto)); + avgTeleopText.setText(String.format("%.2f", avgTele)); + climbRateText.setText(String.format("%.0f%%", climbRate)); + + adapter.updateData(teamMatches); } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout/activity_competition_selection.xml b/app/src/main/res/layout/activity_competition_selection.xml index 21d55a9..fa6f3c1 100644 --- a/app/src/main/res/layout/activity_competition_selection.xml +++ b/app/src/main/res/layout/activity_competition_selection.xml @@ -151,7 +151,13 @@ android:orientation="horizontal" android:gravity="end" android:divider="@drawable/horizontal_padding_small" - android:showDividers="end"> + android:showDividers="middle|end"> +