Tumgik
codemolly · 7 years
Text
Automating data imports to firebase
The ask
We were creating multiple flavors and builds of a tester app, and keeping track of everything was becoming unweildy. Similarly, we were using at least an hour of dev time for each build QA needed. We needed a way for QA to set up the configurations themselves and access them all from a single app rather than having to keep track of multiple builds.
The Idea
This article planted the seed that perhaps the Google Form we were already using could be co-opted to directly send data to Firebase, which could then be parsed by the app.
Google Scripts
Based on the aforementioned article about writing data from an app's script, we're using Google Scripts to do the data transfer. In order to link from the form itself, open the form in edit mode and select Script Editor from the overflow menu. You have to open the script editor from the form and not from scripts.google.com in order to use the Form as a trigger to run your script.
If you want to trigger your script from a Google Sheets document, open the script editor from the sheet's overflow menu just like you would for a Google Form
Reading in data
var ss = SpreadsheetApp.openById(<Sheets ID>); var sheet = ss.getSheets()[0]; var data = sheet.getDataRange().getValues(); for(var i = 1; i < data.length; i++) { // parse rows as Firebase objects }
TODO: I know there's a way to access the form/sheet that triggered the script to run directly rather than referencing a sheet by ID, but I haven't figured out the implementation in my specific use case yet.
I'm pulling the data from the Google sheet that the form data populates to. The Sheets ID is visible in the URL when you open a Google Sheet. The URL will be formatted as https://docs.google.com/spreadsheets/d/<Sheets ID>
Turning Rows into Firebase objects
Think of the data variable in the above example as an array of rows, and each row is an array of cells. So data[n][m] is the contents of the cell at row n, column m. We start at row 1 in our for loop assuming that row 0 is just a row of column headers. Let us say for instance that we want to create a Firebase object that fully reflects data which looks like this:
| Name | Age | Height ft | Height in | Birthdate | |-------|-----|-----------|-----------|-----------| | Alan | 50 | 5 | 8 | Jan 1 | | Bob | 37 | 6 | 2 | Feb 2 | | Carol | 29 | 5 | 11 | Mar 3 |
Each Firebase object will need Name, Age, Height, and Birthdate as keys. Height will contain a sub-object with keys of ft and in. Each object should also have a unique key, which can be anything you determine - in this case we're just going to use row number.
//get data from sheet ... var objects = {}; for(var i = 1; i < data.length; i++) { objects[i] = parseData(data[i]); } // send data to Firebase ... function parseData(Array) { return { name:Array[0], age:Array[1], birthdate:Array[4], height:{ft:Array[2], in:Array[3]} } }
Send Data to Firebase
Per the script example I found, in order to connect to Firebase you need to install the library in your Google Script. In the script editor, open the Resources menu and click Libraries... Paste MYeP8ZEEt1ylVDxS7uyg9plDOcoke7-2l into the box next to Find a Library and press Select. This is the project key for the FirebaseApp plugin library. In the version drop-down menu, choose the most recent public release version and click Save.
Now you will have access to Firebase-specific methods in your script. If you have open write access on your Firebase database, you can write your spreadsheet data like so:
//get data from sheet ... var objects = {}; for(var i = 1; i < data.length; i++) { objects[i] = parseData(data[i]); } var base = FirebaseApp.getDatabaseByUrl(<Your Firebase URL>); base.setData("", objects);
If you require authorization to write to your database (recommended), you'll need to pass the Secret as a second parameter in the call to getDatabaseByUrl. To obtain your secret, open your project in the Firebase console, click the gear icon next to Overview, and select Project Settings. Go to the Service Accounts tab, and select Database Secrets under Legacy credentials. If no secrets are populated, click Add Secret, then click on the row and select Show. Copy this value into a variable in your Google Script and pass it as the second argument in your getDatabaseByUrl call.
Note: this is a deprecated means of authenticating and will likely go away in the future. This document should be updated soon with the new recommended authentication process for scripts.
Trigger
Once you have run your script and determined that it is processing your data as expected, it's time to set up a trigger. In the Resources menu, select current project's triggers. If you have created your script from a Google Sheets document or a Google Form, the Events drop-down menu should be pre-populated with from form or from spreadsheet. There may also be an option to run the script at selected time intervals. Choose the name of your function, and in the case of a Google Form From form and On form submit. Now after you save and close the script, every time you fill out the form your data should automatically be added to your Firebase Database.
0 notes
codemolly · 7 years
Text
Things I had to google this week
Week of 1/9
Lots of time spend rehashing familiar problems at work this week so not a lot of googling. I'll share some links I found working on my personal project as well
long to int - don't judge me, it was before I finished my first coffee
unsubscribe from Firebase notifications
shared preferences mode
SimpleDateFormat - I need to bookmark this. I always forget what letters correspond to what when creating date formats
0 notes
codemolly · 7 years
Text
Things I had to google this week
Week of 1/3
Yay another short week!
c string format specifiers
Specify resource file to load from
So I wanted to have multiple xml files in my values folder with the same ids and choose which one to load Strings/ints from at runtime. Turns out you can't do that. So... wasted googling.
Can I use PowerMock to set the value for Build.MODEL in tests?
no.
mock Methods in android.util.Log
difference between git add --a and git add .
0 notes
codemolly · 7 years
Text
Things I had to google this week
Week of 12/26
Short week at work plus a lot of maintenance makes for a shorter than expected list of googlings.
findbugs error EI_EXPOSE_REP
0 notes
codemolly · 7 years
Text
Things I had to google this week
how to write text to a file
Findbugs error DM_DEFAULT_ENCODING
Could not find method maven() for arguments
no link for this one. A coworker ran into this error and it was resolved by stopping the gradle daemon
Multiple Java exceptions in the same catch block
Zip4J Examples
Set EditText input type
Iterate through jsonObject.keys() (for some reason I always forget how to use while(iterator.hasNext())
0 notes
codemolly · 7 years
Text
One-Step build distribution with Fabric Beta and Jenkins
We’ve been using Jenkins for CI builds for a while now, but have continued to experiment with different methods of distributing builds to our QA team for testing. I had a few requirements to meet for this project:
Automated builds should be created nightly
Builds must automatically be emailed to testers when they complete on Jenkins
We need to increment the version code with each Jenkins build so it’s clear which build is newest
We need to include the changelog from Git in the release notes so it’s clear what changes exist in each build
Automatic Uploads
Because Crashlytics was already integrated in most of our projects, we decided to use Fabric Beta for distribution. Once a project is setup with Crashlytics it is ready for use with Fabric Beta, no extra code changes needed. I added a shell command after ./gradlew assemble to upload the .apk file to Fabric Beta. The command is formatted as crashlyticsUploadDistribution So for instance if I want QA to test our Stage Debug build, the command I run is ./gradlew crashlyticsUploadDistributionStageDebug in the Jenkins shell script. We're only testing debug builds, but some of our projects have multiple flavors that need testing. Under the General tab in the Jenkins project configuration, select "This project is parameterized` and add a Choice Parameter called Flavor. Put in each flavor you support (capitalized) as an option. Then you can set up your Execute Shell Command to use the flavor like this:
chmod +x ./gradlew
./gradlew clean assemble${Flavor}
./gradlew crashlyticsUploadDistribution${Flavor}Debug
Now when you run your Jenkins build, the desired flavor apk will automatically be uploaded and available in the Fabric Beta online console.
Nightly Builds
Thankfully Jenkins does the heavy lifting on this part. In the Build Triggers section of your project Configuration there's a checkbox for Build Periodically. There are a lot of ways you can configure this, but since we wanted nightly builds on weeknights the pattern I went with was H 23 * * 1-5 This builds sometime within the 11pm hour, any day of the month, Monday-Friday. If you're using Choice parameters in your build, the first option should be the one you want automated builds to default to.
Automatic Emails
Now that I've got builds uploading to Fabric Beta every night, I need to get those builds emailed out to our QA team. Fabric has some gradle integrations for distribuiton and release notes. This confused me at first because I wanted to have options for the testers group and obviously release notes would be different for each build, so manually changing these values in the build.gradle file wasn't an option. This is where environment variables comes in. We can pass the variable in our shell command and read it from the gradle script! Perfect. Since I don't want every build to necessarily upload to Fabric (and don't want to crash for missing variables) I optionally set the group name in gradle by adding the following to the defaultConfiguration in my app's build.gradle file:
if (project.hasProperty("CRASHLYTICS_TESTERS")) { ext.betaDistributionGroupAliases ="$CRASHLYTICS_TESTERS" }
Since there are a couple different groups set up on Fabric we might want to use, I added a Choice Parameter called Distribution_List to the Configuration. Then I updated the Jenkins build shell script
chmod +x ./gradlew
./gradlew clean assemble${Flavor} -PCRASHLYTICS_TESTERS=${Distribution_List}
./gradlew crashlyticsUploadDistribution${Flavor}Debug -PCRASHLYTICS_TESTERS=${Distribution_List}
-P followed by the environment variable name indicates to Gradle that this is a build parameter to read at build time. Once the group is set up and I run the Jenkins build, everyone in that group receives an email inviting them to test the app (or letting them know an update is available if they've already accepted the invitation.)
I pass in the release notes similarly using ext.betaDistributionReleaseNotes and a multi-line String build parameter called ReleaseNotes.
Incrementing the version code
Much like the distribution list and release notes, we can pass the Jenkins build number to the gradle script to set the versionCode so we can easily tie builds in Fabric to individual Jenkins builds. It can be done a couple of different ways, but I like the example I found here for the clarity it adds for future developers who may look at my build.gradle file. BUILD_NUMBER is automatically available in Jenkins, so I pass it as an environment variable and boom - now I can easily tell which apk was created when!
Git changes in release notes
This was really the most exciting problem for me to solve, largely because I figured out how to do it pretty much in a single line. Note that this does require that you have the Git plugin set up on your Jenkins org. I'll spare you the whole searching, trial-and-error process and just show you the final product:
GIT_CHANGE_LOG=$(git log --date=short --pretty="%cd %s%n" $GIT_PREVIOUS_SUCCESSFUL_COMMIT..$GIT_COMMIT)
./gradlew clean assemble${Flavor} -PCRASHLYTICS_TESTERS=${Distribution_List} -PRELEASE_NOTES="${Release_Notes} ${GIT_CHANGE_LOG}" -PBUILD_NUMBER=${BUILD_NUMBER}
./gradlew crashlyticsUploadDistribution${Flavor}Debug -PCRASHLYTICS_TESTERS=${Distribution_List} -PRELEASE_NOTES="${Release_Notes} ${GIT_CHANGE_LOG}" -PBUILD_NUMBER=${BUILD_NUMBER}
The first statement creates a variable called GIT_CHANGE_LOG and assigns to it the result of the command in parenthesis. --pretty="%cd %s%n logs the date and commit message of each commit, and setting --date=short leaves the timestamp off the date. $GIT_PREVIOUS_SUCCESSFUL_COMMIT..$GIT_COMMIT tells the shell to only log the data from the commits between the last successful Jenkins build and the current most recent commit. I add that to the RELEASE_NOTES that I pass through gradle to Fabric Beta after the freeform release notes, and the result is automated nightly builds, emailed to a group of my choosing, with release notes that look like this:
Automated nightly build
Fri Dec 9 Merge pull request #11 from mr/fabric to develop
Fri Dec 9 whitespace
Fri Dec 9 condensed beta environment variables
Fri Dec 9 setup Jenkins fabric plugin
And there we have it!
There's a lot you can do with Jenkins as a CI tool, a lot more than I've looked into yet. You can automate builds to go out every time there's a push instead of at a certain time - or in addition. Jenkins also has great integration with HockeyApp as a beta distribution tool. I'm sure my build scripts can be optimized even further, but we've come a long way from emailing our .apk files to the whole team in a short time!
0 notes
codemolly · 8 years
Link
Oh hey, I wrote a thing
0 notes
codemolly · 8 years
Photo
Tumblr media Tumblr media Tumblr media
Awesome built-in wear capability: Adding actions to a notification automatically adds those actions to the wear notification as well! You just swipe the notification to the left once you open it and boom, there they are. I <3 not having to do extra work
0 notes
codemolly · 8 years
Link
I’ve definitely got the anger/tearful frustration thing down. But Reto puts forth an interesting prospect here, for me personally at least. I hate proofreading. Loathe and despise it. My first draft and final papers in English classes through college rarely differed by more than a few words. Something about going over something I already wrote just immediately bores me and fills me with the desire to do literally anything else. That’s part of why I’m such a fan of code review with every commit - if I perfect it now, I never have to go back and look at it again.  I get that this is unrealistic. The question then becomes - how to refactor my own code without wanting to die of boredom?
1 note · View note
codemolly · 8 years
Link
Is anyone else concerned about throwing their shoulder out with these gestures?
0 notes
codemolly · 8 years
Link
Saving this for when I have time to go through it. Parse was the best way for me to get my apps up and running as a beginner, but it totally sucks that I’m going to have to rewrite them now
0 notes
codemolly · 8 years
Text
Getting stuck in an infinite boot loop is fun.
Want to try it? Follow these root instructions: http://www.xda-developers.com/root-unlock-nexus-9-xdatv/
0 notes
codemolly · 8 years
Link
THIS. IS. AWESOME.
0 notes
codemolly · 9 years
Link
This had some really great info on how to use information already built in to iOS to make text layouts better match designs
1 note · View note
codemolly · 9 years
Link
The strides Google is making with image recognition and machine learning are awesome, and I think could also have implications for human learning theory. There are obviously security concerns inherent in having access not only to a user's photos, but days about what they contain. Still, there are some really exciting possibilities for apps if we could access said days, from shopping to insurance claims to games (how cool would an automated scavenger hunt app be?)
0 notes
codemolly · 9 years
Text
I hate when I'm looking for a blog or tutorial on a particular functionality and everything is in broken English. I know that's terribly ethnocentric of me, but I don't find stackoverflow to be all that reliable and a lot of the clarity in these broken English posts is, for lack of a better term, lost in translation. I find that I have this issue much more often when searching for Android blogs than for ios. Basically I want an NSHipster equivalent for Android. And a cookie, while I'm at it.
0 notes
codemolly · 9 years
Text
Did that thing I always do at work again: spend a day stressing out about how what I'm trying to do is clearly impossible, get all pissed off, come back the next day and solve the problem in like 2 hours
0 notes