Skip to content

Commit

Permalink
Merge pull request #251 from spe-uob/250-new-feature-update-database-…
Browse files Browse the repository at this point in the history
…structure-and-instructions-on-importingexporting-data

Update database structure and instructions on exporting/importing data
  • Loading branch information
wwplr authored Jun 2, 2023
2 parents 2e4e28f + 0f0aabe commit d961692
Show file tree
Hide file tree
Showing 44 changed files with 607 additions and 395 deletions.
Binary file modified .DS_Store
Binary file not shown.
14 changes: 3 additions & 11 deletions .github/workflows/CD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,14 @@ jobs:
java-version: '11'
- uses: subosito/flutter-action@v2
- run: flutter pub get
- run: |
flutter build ios --release --no-codesign
cd build/ios/iphoneos
mkdir Release
cd Release
ln -s ../Runner.app
cd ..
zip -r app.ipa Release
- run: flutter build ios --release --no-codesign
- uses: actions/upload-artifact@v3
with:
path: RewardProcessing/build/ios/iphoneos/app.ipa
path: RewardProcessing/build/ios/iphoneos/Runner.app
- uses: softprops/action-gh-release@v1
with:
tag_name: v1.0.${{ github.run_number }}
files: RewardProcessing/build/ios/iphoneos/app.ipa
files: RewardProcessing/build/ios/iphoneos/Runner.app
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand All @@ -72,7 +65,6 @@ jobs:
path: RewardProcessing/build/app/outputs/flutter-apk/app-release.apk
- uses: softprops/action-gh-release@v1
with:

tag_name: v1.0.${{ github.run_number }}
files: RewardProcessing/build/app/outputs/flutter-apk/app-release.apk
env:
Expand Down
228 changes: 110 additions & 118 deletions .idea/libraries/Dart_Packages.xml

Large diffs are not rendered by default.

90 changes: 28 additions & 62 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ To run this application, chance directory to `RewardProcessing` in your terminal
* iOS: `flutter build ios --release --no-codedesign`
* Android: `flutter build apk`

## Exporting Firebase Data
To export data from Firebase, please follow the instructions below:
## Importing Data to Firebase
To ensure all participants enter the correct prolific ID, the IDs should be imported to Firebase in advance. Please follow the instructions below:
1. Install node/npm
1. Mac/Windows: Go to this [link](https://nodejs.org/en/download) to install.
2. Alternatively, for Mac users, you can install via Homebrew .
Expand All @@ -76,70 +76,36 @@ To export data from Firebase, please follow the instructions below:
3. Test it:
* `node -v`
* `npm -v`
*
2. Install jq
1. Mac: `brew install jq`
2. Windows: `choco install jq` If you haven't already installed Chocolatey on your machine, please follow this [link](https://chocolatey.org/install).
3. Set up Firebase credentials
2. Set up Firebase credentials
1. Download the file [here](docs/credentials.json).
* For further details, this file was generated by doing these steps:
* For further information, this file was generated by doing these steps:
1. Generate a private key file for your service account. In the Firebase console, open Settings > Service Accounts.
2. Click Generate New Private Key, then confirm by clicking Generate Key button.
3. Rename the JSON file to `credentials.json`.
4. Use these command lines to export Firebase data to a JSON file:
1. `mkdir [FOLDER]`; create a directory to store a participant's data (e.g., `mkdir Student1`)
2. `npx -p node-firestore-import-export firestore-export -a credentials.json -n [COLLECTION]/[DOCUMENT] -b [FOLDER]/[FILE.json]`,
* `[COLLECTION]` is the name of the collection on Firebase (e.g., questionnaire, game1, game2, feedback)
* `[DOCUMENT]` is the name of the document on Firebase (in this case, the prolific IDs of the participants)
* `[FOLDER]` is where you want the data to be exported to
* `[FILE]` is the name you want for the json file of the exported data
* _Example_: `npx -p node-firestore-import-export firestore-export -a credentials.json -n [questionnaire]/[Student1] -b [Student1]/[q.json]`
3. `cd FOLDER`; change to the directory where your data is saved (e.g., `cd Student1`)
4. `jq . FILE.json > FILE2.json`; organise the data and transfer it to a new json file (e.g., `jq . q.json > questionnaire.json`)
5. `rm FILE.json`; remove the unsorted data file (e.g., `rm q.json`)
* Since each user (Student1) has 4 different data collections (questionnaire, game1, game2, feedback), you can export all of them at the same time for each user (example below). This means each user will have 4 different files in their folder.
```
mkdir Student1
npx -p node-firestore-import-export firestore-export -a credentials.json -n questionnaire/Student1 -b Student1/q.json
npx -p node-firestore-import-export firestore-export -a credentials.json -n game1/Student1 -b Student1/g1.json
npx -p node-firestore-import-export firestore-export -a credentials.json -n game2/Student1 -b Student1/g2.json
npx -p node-firestore-import-export firestore-export -a credentials.json -n feedback/Student1 -b Student1/f.json
cd Student1
jq . q.json > questionnaire.json
jq . g1.json > game1.json
jq . g2.json > game2.json
jq . f.json > feedback.json
rm q.json
rm g1.json
rm g2.json
rm f.json
```
* [This](docs/Student1) is an example of the exported files.

## Importing to Firebase
This is for the clients to import a list of prolific IDs onto the database's document to check for validation when participants use the application.
1. Follow the first two steps of [Exporting Firebase Data](#Exporting-Firebase-Data) if you haven't already.
2. Use this command line to import a JSON file to Firebase's document section
1. `npx -p node-firestore-import-export firestore-import -a credentials.json -n [COLLECTION] -b [FILE].json`
* `[COLLECTION]` is the name of the collection on Firebase (e.g., questionnaire, game1, game2, feedback)
* However, in this case, the file could be imported to the `questionnaire` collection only since the prolific ID the participants enter will be checked for validation in this collection and the data will be stored in the other collections later as the participants get to that part of the app.
* `[FILE]` is the JSON file of the client's prolific IDs.
2. The file format should be like this:
```
{
"Student1": {},
"Student2": {},
"Student3": {},
"Student4": {}
}
```
[This](docs/prolific_ids.json) is the JSON file.
* There should **NOT** be an extra comma at the end.
* `Student1`, `Student2`, `Student3`, and `Student4` are the prolific IDs that will be stored as the `[DOCUMENT]` on Firebase with empty data `{}` inside it.
3. Example of the imported documents:
<a name="readme-top"></a><p align="center">
![Contributors](docs/firebase_import.png)
</a>
3. Install the Firebase CLI tool
1. Follow the instructions provided by Firebase [here](https://firebase.google.com/docs/cli#install_the_firebase_cli).
2. Once the Firebase CLI is installed, open your terminal or command prompt.
3. Log in to Firebase using the following command: `firebase login`.
4. Import data to Firebase using the given JavaScript code (import.js):
1. Download the file [here](docs/import.js).
2. Enter the Prolific IDs at `Line 36` as demonstrated in the file.
* Make sure that there is no comma at the end of the array
* ```
const documentIds = [
'ProlificID1',
'ProlificID2',
'ProlificID3' // no comma here
];
```
3. Run `node import.js`.
## Exporting Data From Firebase
To export the data from Firebase into JSON files, please follow the instructions below:
1. If you have not already, please follow the first three steps of [Exporting Firebase Data](#Exporting-Firebase-Data).
2. Export data using the given JavaScript code (export.js):
1. Download the file [here](docs/export.js).
2. Run `node export.js`.
* [This](docs/Student1) is an example of the exported files.
## Our Clients
* Conor Houghton - an associate professor in Computer Science at University of Bristol
Expand Down
3 changes: 2 additions & 1 deletion RewardProcessing/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
Expand Down Expand Up @@ -204,6 +204,7 @@
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
9 changes: 6 additions & 3 deletions RewardProcessing/lib/game/aim.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import 'package:rewardprocessing/game/game_map.dart';

class Aim extends StatefulWidget {
final String id;
const Aim({super.key, required this.id});
final String day;
const Aim({super.key, required this.id, required this.day});

@override
State<Aim> createState() => _AimState();
Expand Down Expand Up @@ -137,11 +138,13 @@ class _AimState extends State<Aim> {
onPressed: () async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => GameMap(id: widget.id))
MaterialPageRoute(builder: (context) => GameMap(id: widget.id, day: widget.day))
);
await FirebaseFirestore.instance
.collection('game1')
.collection('participants')
.doc(widget.id)
.collection(widget.day)
.doc('game1')
.set({'0. Game Details': [
'Site Switch Probability: 0.3',
'Cherry Probability: 0.8',
Expand Down
9 changes: 6 additions & 3 deletions RewardProcessing/lib/game/game_finished.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import 'package:flutter/services.dart';

class GameFinished extends StatefulWidget {
final String id;
const GameFinished({super.key, required this.id});
final String day;
const GameFinished({super.key, required this.id, required this.day});

@override
State<GameFinished> createState() => _GameFinishedState();
Expand Down Expand Up @@ -54,11 +55,13 @@ class _GameFinishedState extends State<GameFinished> {
onPressed: () async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => GameMap2(id: widget.id))
MaterialPageRoute(builder: (context) => GameMap2(id: widget.id, day: widget.day))
);
await FirebaseFirestore.instance
.collection('game2')
.collection('participants')
.doc(widget.id)
.collection(widget.day)
.doc('game2')
.set({'0. Game Details': [
'Site Switch Probability: 0.3',
'Cherry Probability: 0.8',
Expand Down
5 changes: 3 additions & 2 deletions RewardProcessing/lib/game/game_finished2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import 'package:rewardprocessing/questionnaire/finishedquestion.dart';

class GameFinished2 extends StatefulWidget {
final String id;
const GameFinished2({super.key, required this.id});
final String day;
const GameFinished2({super.key, required this.id, required this.day});

@override
State<GameFinished2> createState() => _GameFinishedState2();
Expand Down Expand Up @@ -54,7 +55,7 @@ class _GameFinishedState2 extends State<GameFinished2> {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => FinishedQuestion(
id: widget.id)
id: widget.id, day: widget.day)
)
);
},
Expand Down
16 changes: 9 additions & 7 deletions RewardProcessing/lib/game/game_instructions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import 'package:flutter/services.dart';

class GameInstructions extends StatefulWidget {
final String id;
const GameInstructions({super.key, required this.id});
final String day;
const GameInstructions({super.key, required this.id, required this.day});

@override
State<GameInstructions> createState() => _GameInstructionsState();
Expand Down Expand Up @@ -35,22 +36,23 @@ class _GameInstructionsState extends State<GameInstructions> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset('assets/images/gi1.png',
scale: 3,
scale: 3.75,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
// MaterialPageRoute(builder: (context) => const Questionnaire())
MaterialPageRoute(builder: (context) => GameInstructions2(id: widget.id))
MaterialPageRoute(builder: (context) => GameInstructions2(id: widget.id, day: widget.day))
);
},
style: ElevatedButton.styleFrom(
fixedSize: const Size(100, 20),
fixedSize: const Size(110, 25),
backgroundColor: const Color(0xFF00A8AF),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100)),
elevation: 2.0,),
borderRadius: BorderRadius.circular(100)
),
elevation: 2.0
),
child: const Text(
'Continue',
textAlign: TextAlign.center,
Expand Down
17 changes: 10 additions & 7 deletions RewardProcessing/lib/game/game_instructions2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import 'package:flutter/services.dart';

class GameInstructions2 extends StatefulWidget {
final String id;
const GameInstructions2({super.key, required this.id});
final String day;
const GameInstructions2({super.key, required this.id, required this.day});

@override
State<GameInstructions2> createState() => _GameInstructions2State();
Expand Down Expand Up @@ -35,22 +36,24 @@ class _GameInstructions2State extends State<GameInstructions2> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset('assets/images/gi2.png',
scale: 3,
scale: 3.75,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
// MaterialPageRoute(builder: (context) => const Questionnaire())
MaterialPageRoute(builder: (context) => GameInstructions3(id: widget.id))
MaterialPageRoute(builder: (context) => GameInstructions3(id: widget.id, day: widget.day)
)
);
},
style: ElevatedButton.styleFrom(
fixedSize: const Size(100, 20),
fixedSize: const Size(110, 25),
backgroundColor: const Color(0xFF00A8AF),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100)),
elevation: 2.0,),
borderRadius: BorderRadius.circular(100)
),
elevation: 2.0
),
child: const Text(
'Continue',
textAlign: TextAlign.center,
Expand Down
12 changes: 7 additions & 5 deletions RewardProcessing/lib/game/game_instructions3.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import 'package:flutter/services.dart';

class GameInstructions3 extends StatefulWidget {
final String id;
const GameInstructions3({super.key, required this.id});
final String day;
const GameInstructions3({super.key, required this.id, required this.day});

@override
State<GameInstructions3> createState() => _GameInstructions3State();
Expand Down Expand Up @@ -35,21 +36,22 @@ class _GameInstructions3State extends State<GameInstructions3> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset('assets/images/gi3.png',
scale: 3,
scale: 3.75,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
// MaterialPageRoute(builder: (context) => const Questionnaire())
MaterialPageRoute(builder: (context) => GameInstructions4(id: widget.id))
MaterialPageRoute(builder: (context) => GameInstructions4(id: widget.id, day: widget.day))
);
},
style: ElevatedButton.styleFrom(
fixedSize: const Size(100, 20),
fixedSize: const Size(110, 25),
backgroundColor: const Color(0xFF00A8AF),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100)),
borderRadius: BorderRadius.circular(100)
),
elevation: 2.0,),
child: const Text(
'Continue',
Expand Down
13 changes: 7 additions & 6 deletions RewardProcessing/lib/game/game_instructions4.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import 'package:flutter/services.dart';

class GameInstructions4 extends StatefulWidget {
final String id;
const GameInstructions4({super.key, required this.id});
final String day;
const GameInstructions4({super.key, required this.id, required this.day});

@override
State<GameInstructions4> createState() => _GameInstructions4State();
Expand Down Expand Up @@ -35,21 +36,21 @@ class _GameInstructions4State extends State<GameInstructions4> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset('assets/images/gi4.png',
scale: 3,
scale: 3.75,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
// MaterialPageRoute(builder: (context) => const Questionnaire())
MaterialPageRoute(builder: (context) => GameInstructions5(id: widget.id))
MaterialPageRoute(builder: (context) => GameInstructions5(id: widget.id, day: widget.day))
);
},
style: ElevatedButton.styleFrom(
fixedSize: const Size(100, 20),
fixedSize: const Size(110, 25),
backgroundColor: const Color(0xFF00A8AF),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100)),
borderRadius: BorderRadius.circular(100)
),
elevation: 2.0,),
child: const Text(
'Continue',
Expand Down
Loading

0 comments on commit d961692

Please sign in to comment.