Skip to content

feat: added csv import/export for robotic arm #2807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 10, 2025

Conversation

rahul31124
Copy link
Contributor

@rahul31124 rahul31124 commented Jul 30, 2025

Changes

  • Added CSV import/export functionality for the robotic arm. Users can now import timeline CSV files externally (e.g., from PSLab Python) and export them as well.

Recording

robotic_arm_csv.webm

Checklist:

  • No hard coding: I have used resources from strings.xml, dimens.xml and colors.xml without hard coding any value.
  • No end of file edits: No modifications done at end of resource files strings.xml, dimens.xml or colors.xml.
  • Code reformatting: I have reformatted code and fixed indentation in every file included in this pull request.
  • No extra space: My code does not contain any extra lines or extra spaces than the ones that are necessary.

Summary by Sourcery

Add CSV import and export capabilities for the robotic arm timeline, including UI prompts for file naming, provider methods for CSV handling, and integration with the logged data screen to load external CSVs.

New Features:

  • Export the robotic arm timeline to a CSV file with a filename input dialog and feedback snackbar
  • Import a CSV timeline via the Logged Data screen and apply it to the robotic arm state
  • Add CsvService integration for reading and writing CSV files

Enhancements:

  • Adjust LoggedDataScreen to return CSV data directly for the robotic arm
  • Introduce date formatting for CSV timestamp entries

Copy link
Contributor

sourcery-ai bot commented Jul 30, 2025

Reviewer's Guide

Implements CSV import/export for the robotic arm by integrating a CsvService in the state provider, adding UI dialogs and menu actions in RoboticArmScreen to trigger export/import operations with filename input and feedback, and updating LoggedDataScreen to return CSV data directly for robotic arm instead of chart navigation.

Sequence diagram for robotic arm CSV export process

sequenceDiagram
    actor User
    participant RoboticArmScreen
    participant RoboticArmStateProvider
    participant CsvService
    User->>RoboticArmScreen: Tap save icon
    RoboticArmScreen->>User: Show filename dialog
    User->>RoboticArmScreen: Enter filename and confirm
    RoboticArmScreen->>RoboticArmStateProvider: exportTimelineToCsv(filename)
    RoboticArmStateProvider->>CsvService: saveCsvFile(...)
    CsvService-->>RoboticArmStateProvider: File saved
    RoboticArmStateProvider-->>RoboticArmScreen: Success/failure
    RoboticArmScreen->>User: Show snackbar with result
Loading

Sequence diagram for robotic arm CSV import process

sequenceDiagram
    actor User
    participant RoboticArmScreen
    participant LoggedDataScreen
    participant RoboticArmStateProvider
    User->>RoboticArmScreen: Open menu, select 'Show Logged Data'
    RoboticArmScreen->>LoggedDataScreen: Navigate to
    User->>LoggedDataScreen: Select CSV file
    LoggedDataScreen-->>RoboticArmScreen: Return CSV data
    RoboticArmScreen->>RoboticArmStateProvider: importTimelineFromCsv(data)
    RoboticArmStateProvider-->>RoboticArmScreen: Update timeline
    RoboticArmScreen->>User: Updated timeline displayed
Loading

Class diagram for CSV import/export integration in RoboticArmStateProvider

classDiagram
    class RoboticArmStateProvider {
      +Future<void> exportTimelineToCsv(String instrumentName, String fileName)
      +Future<void> importTimelineFromCsv(List<List<dynamic>> csv)
      -CsvService _csvService
      List<List<double?>> timelineDegrees
    }
    class CsvService {
      +Future<void> saveCsvFile(String instrumentName, String fileName, List<List<dynamic>> data)
      +Future<List<List<dynamic>>> readCsvFromFile(File file)
      +Future<List<List<dynamic>>?> pickAndReadCsvFile()
    }
    RoboticArmStateProvider --> CsvService : uses
Loading

File-Level Changes

Change Details Files
Add UI for CSV import/export in RoboticArmScreen
  • Imported LoggedDataScreen and updated AppBar title localization
  • Implemented save button to show filename input dialog, call exportTimelineToCsv, and display SnackBar feedback
  • Extended popup menu to navigate to LoggedDataScreen and import CSV data via provider
lib/view/robotic_arm_screen.dart
Extend RoboticArmStateProvider with CSV export/import methods
  • Injected CsvService and added exportTimelineToCsv using DateFormat and timelineDegrees data
  • Implemented importTimelineFromCsv to parse CSV rows into timelineDegrees and notify listeners
  • Reordered initialization to call _initTimelineDegrees after localization setup
lib/providers/robotic_arm_state_provider.dart
Update LoggedDataScreen to return CSV data for robotic arm
  • Modified _openFile to pop CSV data to previous route when instrumentName is 'robotic arm'
  • Adjusted _pickAndImportFile similarly, retaining chart navigation for other instruments
lib/view/logged_data_screen.dart
Adjust localization key for RoboticArmScreen title
  • Replaced appLocalizations.roboticArm with appLocalizations.roboticArmTitle
lib/view/robotic_arm_screen.dart

Possibly linked issues

  • Add first version of Android App #1: PR implements CSV import/export for robotic arm data, directly fulfilling a key export functionality improvement.
  • #0: The PR implements CSV import/export for robotic arm timeline data, directly addressing the issue's goal.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @rahul31124 - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `lib/providers/robotic_arm_state_provider.dart:228` </location>
<code_context>
+
+    final dateFormat = DateFormat('yyyy-MM-dd HH:mm:ss.SSS');
+
+    for (int i = 0; i < timelineDegrees.length; i++) {
+      final row = timelineDegrees[i];
+      final timestamp = i;
+      final dateTime = dateFormat.format(DateTime.now());
+
+      csvData.add([
</code_context>

<issue_to_address>
DateTime.now() used for each row may not reflect actual timeline data.

Consider moving DateTime.now() outside the loop if you want a single export timestamp, or use the actual data's timestamp for each row if available.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    final dateFormat = DateFormat('yyyy-MM-dd HH:mm:ss.SSS');

    for (int i = 0; i < timelineDegrees.length; i++) {
      final row = timelineDegrees[i];
      final timestamp = i;
      final dateTime = dateFormat.format(DateTime.now());

      csvData.add([
        timestamp,
        row[0]?.toString() ?? 'null',
        row[1]?.toString() ?? 'null',
        row[2]?.toString() ?? 'null',
        row[3]?.toString() ?? 'null',
        dateTime
      ]);
    }
=======
    final dateFormat = DateFormat('yyyy-MM-dd HH:mm:ss.SSS');
    final exportDateTime = dateFormat.format(DateTime.now());

    for (int i = 0; i < timelineDegrees.length; i++) {
      final row = timelineDegrees[i];
      final timestamp = i;

      csvData.add([
        timestamp,
        row[0]?.toString() ?? 'null',
        row[1]?.toString() ?? 'null',
        row[2]?.toString() ?? 'null',
        row[3]?.toString() ?? 'null',
        exportDateTime
      ]);
    }
>>>>>>> REPLACE

</suggested_fix>

### Comment 2
<location> `lib/providers/robotic_arm_state_provider.dart:247` </location>
<code_context>
+  }
+
+  Future<void> importTimelineFromCsv(List<List<dynamic>> csv) async {
+    if (csv.length <= 2) return;
+
+    final dataRows = csv.skip(1).toList();
</code_context>

<issue_to_address>
Header skipping logic may be off by one.

The guard clause may prevent processing a CSV with only one data row. Consider changing the check to csv.length <= 1 to ensure single-row CSVs are handled.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
  Future<void> importTimelineFromCsv(List<List<dynamic>> csv) async {
    if (csv.length <= 2) return;

    final dataRows = csv.skip(1).toList();
=======
  Future<void> importTimelineFromCsv(List<List<dynamic>> csv) async {
    if (csv.length <= 1) return;

    final dataRows = csv.skip(1).toList();
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@rahul31124 rahul31124 requested a review from CloudyPadmal July 30, 2025 17:06
Copy link

github-actions bot commented Jul 30, 2025

@marcnause marcnause self-requested a review August 3, 2025 15:09
@marcnause marcnause enabled auto-merge (squash) August 10, 2025 09:10
@marcnause marcnause merged commit f1cf3f8 into fossasia:flutter Aug 10, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants