remove all of fitbit (#482)

* remove all of fitbit

* try fixing migration

* add new structure sql

* 🐶

* add first test for das

* 🐶

* fix structure.sql
This commit is contained in:
Bastian Greshake Tzovaras
2018-01-19 11:29:07 -08:00
committed by GitHub
parent 293926c199
commit 44184dda33
29 changed files with 40 additions and 987 deletions

View File

@@ -1,19 +0,0 @@
.fitbit__download-button
width: 175px
margin-bottom: 10px
.fitbit__download-text
font-size: 10px
.fitbit__download-container
margin-top: 30px
.fitbit__table
clear: both
margin-top: 30px
.fitbit__form
margin: 30px 0
.fitbit__form-submit-button
margin-top: 20px

View File

@@ -1,138 +0,0 @@
# frozen_string_literal: true
class FitbitProfilesController < ApplicationController
before_action :require_user, except: [:show, :index]
helper_method :sort_column, :sort_direction
def index
@title = 'Listing all connected Fitbit accounts'
@fitbit_profiles = FitbitProfile
.includes(:user)
.order("#{sort_column} #{sort_direction}")
.paginate(page: params[:page], per_page: 15)
end
def show
@fitbit_profile = FitbitProfile.find_by_id(params[:id]) || not_found
@title = 'Fitbit profile'
#grab activity measures for graphs
if @fitbit_profile.activities
@activity = FitbitActivity
.where(fitbit_profile_id: @fitbit_profile.id)
.order(:date_logged)
@total_length = 0 # sum of all steps which are not 0 and not nil
@total_floors = []
@total_steps = []
@floors = []
@steps = []
@floor_counter = 0
@step_counter = 0
@activity.each do |a|
# Sometimes, floors is nil and not a number - API problem?
# Dismiss these entries
if a.steps.nil? or a.floors.nil?
next
end
if a.steps != 0
@total_length += 1
end
@total_floors << [a.date_logged, @floor_counter += a.floors]
@floors << [a.date_logged, a.floors]
@steps << [a.date_logged, a.steps]
@total_steps << [a.date_logged, @step_counter += a.steps]
end
if not @total_steps.empty?
begin
@mean_steps = @total_steps[-1][-1] / @total_length #@activity.length
rescue
end
end
end
#grab body measurements for graphs
if @fitbit_profile.body
@body = FitbitBody
.where(fitbit_profile_id: @fitbit_profile.id)
.order(:date_logged)
@bmi = @body.map {|fa| [fa.date_logged, fa.bmi]}
end
#grab sleep measurements for graphs
if @fitbit_profile.sleep
@sleep = FitbitSleep
.where(fitbit_profile_id: @fitbit_profile.id)
.order(:date_logged)
@total_minutes_asleep = []
@total_minutes_to_sleep = []
@minutes_asleep = []
@minutes_to_sleep = []
@awakenings = []
@total_to_sleep_counter = 0
@total_asleep_counter = 0
@no_sleep = 0
@sleep.each do |s|
# Here again, some have nils
# Skip these
if s.minutes_to_sleep.nil? or s.minutes_asleep.nil?
next
end
if s.minutes_asleep == 0
@no_sleep += 1
end
@total_minutes_to_sleep << [s.date_logged, @total_to_sleep_counter += s.minutes_to_sleep]
@total_minutes_asleep << [s.date_logged, @total_asleep_counter += s.minutes_asleep]
@minutes_asleep << [s.date_logged, s.minutes_asleep]
@minutes_to_sleep << [s.date_logged, s.minutes_to_sleep]
@awakenings << [s.date_logged, s.number_awakenings]
end
if not @total_minutes_asleep.empty?
begin
@mean_sleep = @total_minutes_asleep[-1][-1] / (@sleep.length - @no_sleep)
rescue
end
end
end
end
def dump
@fitbit_profile = FitbitProfile.find_by_id(params[:id]) || not_found
FitbitDump.perform_async(@fitbit_profile.id,current_user.id)
end
def info
end
private
def require_owner
unless current_user == FitbitProfile.find(params[:fitbit_profile][:id]).user.id
store_location
if current_user
return true
else
flash[:notice] = 'You need to be logged in'
redirect_to '/signin'
end
return false
end
end
def sort_column
Genotype.column_names.include?(params[:sort]) ? params[:sort] : 'created_at'
end
def sort_direction
%w[desc asc].include?(params[:direction]) ? params[:direction] : 'desc'
end
end

View File

@@ -127,7 +127,7 @@ class UsersController < ApplicationController
@user = User.find(params[:id])
if params[:user][:user_phenotypes_attributes].present?
params[:user][:user_phenotypes_attributes].each do |p|
params[:user][:user_phenotypes_attributes].each do |p|
@phenotype = UserPhenotype.find(p[1]["id"]).phenotype
@old_variation = UserPhenotype.find_by_id(p[1]["id"]).variation
end
@@ -186,10 +186,6 @@ class UsersController < ApplicationController
flash[:notice] = 'Thank you for using openSNP. Goodbye!'
# disconnect from fitbit if needed
if @user.fitbit_profile != nil
Sidekiq::Client.enqueue(FitbitEndSubscription, @user.fitbit_profile.id)
end
@user.destroy

View File

@@ -114,13 +114,6 @@ class UserMailer < ActionMailer::Base
mail(subject: 'openSNP.org: Sorry, there is no data to be dumped', to: target_address)
end
def fitbit_dump(link, user_id)
@link = link
@user = User.find(user_id)
mail(subject: 'openSNP.org: The Fitbit-data you requested is now ready for download',
to: @user.email)
end
def finished_parsing(genotype_id, stats)
genotype = Genotype.find(genotype_id)
@user = genotype.user

View File

@@ -1,10 +0,0 @@
# frozen_string_literal: true
class FitbitActivity < ActiveRecord::Base
belongs_to :fitbit_profile
def self.find_or_create_by_fitbit_profile_id_and_date_logged(fitbit_profile_id, date_logged)
obj = self.find_by_fitbit_profile_id_and_date_logged( fitbit_profile_id, date_logged ) || self.new(fitbit_profile_id: fitbit_profile_id, date_logged: date_logged)
obj
end
end

View File

@@ -1,10 +0,0 @@
# frozen_string_literal: true
class FitbitBody < ActiveRecord::Base
belongs_to :fitbit_profile
def self.find_or_create_by_fitbit_profile_id_and_date_logged(fitbit_profile_id, date_logged)
obj = self.find_by_fitbit_profile_id_and_date_logged( fitbit_profile_id, date_logged ) || self.new(fitbit_profile_id: fitbit_profile_id, date_logged: date_logged)
obj
end
end

View File

@@ -1,7 +0,0 @@
# frozen_string_literal: true
class FitbitProfile < ActiveRecord::Base
belongs_to :user
has_many :fitbit_bodies, dependent: :destroy
has_many :fitbit_activities, dependent: :destroy
has_many :fitbit_sleeps, dependent: :destroy
end

View File

@@ -1,10 +0,0 @@
# frozen_string_literal: true
class FitbitSleep < ActiveRecord::Base
belongs_to :fitbit_profile
def self.find_or_create_by_fitbit_profile_id_and_date_logged(fitbit_profile_id, date_logged)
obj = self.find_by_fitbit_profile_id_and_date_logged( fitbit_profile_id, date_logged ) || self.new(fitbit_profile_id: fitbit_profile_id, date_logged: date_logged)
obj
end
end

View File

@@ -35,7 +35,6 @@ class User < ActiveRecord::Base
has_many :snp_comments # these shouldn't be deleted, but orphaned
has_many :phenotype_comments, dependent: :destroy
has_many :picture_phenotype_comments, dependent: :destroy
has_one :fitbit_profile, dependent: :destroy
has_one :open_humans_profile, dependent: :destroy
# needed to edit several user_phenotypes at once, add and delete, and not empty

View File

@@ -1,43 +0,0 @@
<div class="general__container">
<div class="row">
<div class="col-md-6">
<h3><%=image_tag("fitbit-icon.png")%> Listing all connected <em>Fitbit</em> accounts </h3>
<h4><a href="https://github.com/superbobry/snpy">Python-library to parse the provided files,</a> courtesy of <a href="https://github.com/superbobry/">Sergei Lebedev</a></h4>
</div>
<div class="fitbit__download-container col-md-6 ">
<%= link_to Zipfulldata.public_path, title: "Request download", class: "btn btn-default center-block fitbit__download-button" do %>
Download dump
<% end %>
<p class="text-center fitbit__text-download">Includes all genotyping files plus a CSV with all phenotypes of those users</p>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover snps__table" id="all_fitbit">
<thead>
<tr>
<th>User</th>
<th>#</th>
<th><%= sortable "fitbit_user_id", "Fitbit ID"%></th>
<th><%= sortable "activities", "Shares Activities"%></th>
<th><%= sortable "body", "Shares Body Data"%></th>
<th><%= sortable "sleep", "Shares Sleep Data"%></th>
<th>View Profile</th>
</tr>
</thead>
<% @fitbit_profiles.each_with_index do |fitbit_profile, i| %>
<tr>
<td class="table-cell vertical-centered"><%= link_to(image_tag(fitbit_profile.user.avatar.url(:head), :class => "img-circle", :width => "50px") + " #{fitbit_profile.user.name}", fitbit_profile.user) %></td>
<td class="table-cell vertical-centered"><%= table_row_sequence_number(@fitbit_profiles, i) %></td>
<td class="table-cell vertical-centered"><%= link_to(fitbit_profile.fitbit_user_id, {:controller => "fitbit_profiles", :action => "show", :id => fitbit_profile.id}) %></td>
<td class="table-cell vertical-centered"><%if fitbit_profile.activities == true%><i class="icon-ok"></i><%else%><i class="icon-remove"></i><%end%></td>
<td class="table-cell vertical-centered"><%if fitbit_profile.body == true%><i class="icon-ok"></i><%else%><i class="icon-remove"></i><%end%></td>
<td class="table-cell vertical-centered"><%if fitbit_profile.sleep == true%><i class="icon-ok"></i><%else%><i class="icon-remove"></i><%end%></td>
<td class="table-cell vertical-centered"><%= link_to("View", {:controller => "fitbit_profiles", :action => "show", :id => fitbit_profile.id}, class: "btn btn-default")%></td>
</tr>
<% end %>
</table>
<div class="text-center">
<%= page_navigation_links @fitbit_profiles%>
</div>
</div>
</div>

View File

@@ -1,231 +0,0 @@
<div class="general__container">
<h3 class="general__title"><%=image_tag("fitbit-icon.png")%> <em>"Fitbit"</em> profile for <%= link_to(@fitbit_profile.user.name, @fitbit_profile.user)%></h3>
<%if current_user%>
<p class="lead"><%=link_to("Download dump",{:controller => "fitbit_profiles", :action => "dump", :id => @fitbit_profile.id}, class: "btn btn-default")%></p>
<%else%>
<p class="lead">If you log in to <em>openSNP</em>, you can download a CSV file with all the data.</p>
<%end%>
<%if @activity != nil%>
<legend>Activity Measurements</legend>
<script type="text/javascript">
$(document).ready(function(){
// Activity Graph
var steps=<%=raw(@steps.to_json)%>;
var floors=<%=raw(@floors.to_json)%>;
var plot1 = $.jqplot('chart1', [floors,steps], {
title:'Activity per Day (Mean steps/day: <%=@mean_steps%>)',
series:[{label:'Floors' ,yaxis:'y2axis',fill: true,fillAlpha: 0.8,highlightMouseOver: false}, {label:'Steps'}],
axes:{
xaxis:{
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{
formatString:'%b&nbsp;%#d'
}
},
yaxis:{
tickOptions:{
formatString:'%i steps'
}
},
y2axis:{
tickOptions:{
formatString:'%i floors'
},
},
},
axesDefaults: {
pad: 0
},
legend: {
show: true,
placement: 'outsideGrid'
},
highlighter: {
show: true,
sizeAdjust: 7.5
},
cursor: {
show: false
}
});
// Sum Activity Graph
var total_steps=<%=raw(@total_steps.to_json)%>;
var total_floors=<%=raw(@total_floors.to_json)%>;
var plot2 = $.jqplot('chart2', [total_floors,total_steps], {
title:'Sum of Activity',
series:[{label:"Floors",yaxis:'y2axis',fill:true,fillAlpha:0.5},{label:'Steps',fill:true,fillAlpha:0.5}],
axes:{
xaxis:{
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{
formatString:'%b&nbsp;%#d'
}
},
yaxis:{
tickOptions:{
formatString:'%i steps'
}
},
y2axis:{
tickOptions:{
formatString:'%i floors'
},
},
},
axesDefaults: {
pad: 0
},
legend: {
show: true,
placement: 'outsideGrid'
},
highlighter: {
show: true,
sizeAdjust: 7.5
},
cursor: {
show: false
}
});
});
</script>
<div id="chart1"></div>
<div id="chart2"></div>
<%end%>
<%if @body and @body != [] %>
<legend>Body Measurements</legend>
<script type="text/javascript">
$(document).ready(function(){
// BMI Graph
var bmi=<%=raw(@bmi.to_json)%>;
var plot3 = $.jqplot('chart3', [bmi], {
title:'BMI per Day',
series:[{label:'Body Mass Index',fill:true}],
axes:{
xaxis:{
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{
formatString:'%b&nbsp;%#d'
}
},
yaxis:{
tickOptions:{
formatString:'%.2f BMI'
}
},
},
axesDefaults: {
},
legend: {
show: true,
placement: 'outsideGrid'
},
highlighter: {
show: true,
sizeAdjust: 7.5
},
cursor: {
show: false
}
});
});
</script>
<div id="chart3"></div>
<%end%>
<%if @sleep and @sleep != [] %>
<legend>Sleep Measurements</legend>
<script type="text/javascript">
$(document).ready(function(){
// Activity Graph
var minutes_asleep=<%=raw(@minutes_asleep.to_json)%>;
var minutes_to_sleep=<%=raw(@minutes_to_sleep.to_json)%>;
var awakenings=<%=raw(@awakenings.to_json)%>;
var plot4 = $.jqplot('chart4', [awakenings,minutes_asleep,minutes_to_sleep], {
title:'Sleep per Day (Mean sleep/day: <%=@mean_sleep%> minutes)',
series:[{label:'# awaken' ,yaxis:'y2axis',fill: true,highlightMouseOver: false}, {label:'Minutes asleep'},{label: 'Minutes awake',fill:true}],
axes:{
xaxis:{
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{
formatString:'%b&nbsp;%#d'
}
},
yaxis:{
tickOptions:{
formatString:'%i minutes'
}
},
y2axis:{
tickOptions:{
formatString:'%i times awoken'
},
},
},
axesDefaults: {
pad: 0
},
legend: {
show: true,
placement: 'outsideGrid'
},
highlighter: {
show: true,
sizeAdjust: 7.5
},
cursor: {
show: false
}
});
// Sum Activity Graph
var total_minutes_asleep=<%=raw(@total_minutes_asleep.to_json)%>;
var total_minutes_to_sleep=<%=raw(@total_minutes_to_sleep.to_json)%>;
var plot5 = $.jqplot('chart5', [total_minutes_asleep,total_minutes_to_sleep], {
title:'Sum of Sleep',
series:[{label:'minutes asleep',fill:true,fillAlpha:0.5},{label:"minutes falling asleep",yaxis:'y2axis',fill:true,fillAlpha:0.6}],
axes:{
xaxis:{
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{
formatString:'%b&nbsp;%#d'
}
},
yaxis:{
tickOptions:{
formatString:'%i min (asleep)'
}
},
y2axis:{
tickOptions:{
formatString:'%i min (falling asleep)'
},
},
},
axesDefaults: {
pad: 0
},
legend: {
show: true,
placement: 'outsideGrid'
},
highlighter: {
show: true,
sizeAdjust: 7.5
},
cursor: {
show: false
}
});
});
</script>
<div id="chart4"></div>
<div id="chart5"></div>
<%end%>
<script type="text/javascript">
$(document).ready(function () {
$("[rel=tooltip]").tooltip({placement:'right'});
});
</script>
</div>

View File

@@ -20,7 +20,6 @@
<li><%= link_to "Phenotypes Pictures", {:controller => "picture_phenotypes", :action => "index"}, class: "navbar-menu__options"%></li>
<li role="separator" class="divider"></li>
<li><%= link_to "Open Humans Connections", {:controller => "open_humans_profiles", :action => "index"}, class: "navbar-menu__options"%></li>
<li><%= link_to "Fitbit Connections", {:controller => "fitbit_profiles", :action => "index"}, class: "navbar-menu__options"%></li>
</ul>
</li>
<li><%= link_to "Latest Data", "/updates", class: "navbar-menu__options"%></li>

View File

@@ -70,7 +70,7 @@
<% end %>
</div>
<% if ["phenotypes", "picture_phenotypes", "fitbit_profiles", "open_humans_profiles"].include? params[:controller] %>
<% if ["phenotypes", "picture_phenotypes", "open_humans_profiles"].include? params[:controller] %>
<div class="container-fluid">
<div class="row">
<div class="col-md-2 col-xs-6 col-xs-offset-2 col-md-offset-0">
@@ -87,9 +87,6 @@
<li <% if params[:controller] == "open_humans_profiles" %>class="active"<% end %>>
<a class="phenotype__side-menu-item" href="/openhumans">Open Humans</a>
</li>
<li <% if params[:controller] == "fitbit_profiles" %>class="active"<% end %>>
<a class="phenotype__side-menu-item" href="/fitbit">Fitbit phenotypes</a>
</li>
</ul>
</div>

View File

@@ -3,7 +3,7 @@
<ul class="nav nav-tabs faq__questions-tabs" role="tablist">
<li role="presentation" class="active"><a href="#updating-data" aria-controls="home" role="tab" data-toggle="tab">Uploading data</a></li>
<li role="presentation"><a href="#downloading-data" aria-controls="profile" role="tab" data-toggle="tab">Downloading data</a></li>
<li role="presentation"><a href="#phenotype-fitbit" aria-controls="messages" role="tab" data-toggle="tab">Phenotypes & Fitbit</a></li>
<li role="presentation"><a href="#phenotype-fitbit" aria-controls="messages" role="tab" data-toggle="tab">Phenotypes</a></li>
<li role="presentation"><a href="#filetypes" aria-controls="settings" role="tab" data-toggle="tab">Filetypes</a></li>
<li role="presentation"><a href="#json-api" aria-controls="settings" role="tab" data-toggle="tab">JSON-API</a></li>
<li role="presentation"><a href="#distributed-system" aria-controls="settings" role="tab" data-toggle="tab">Distributed Annotation System</a></li>
@@ -391,96 +391,6 @@
</div>
</div>
</li>
<li class="faq__single-question ">
<div class='panel panel-default faq__single-question-div'>
<div class='panel-heading faq__single-question-panel'>
<h4 class="panel-title clearfix faq__question-title">
<a data-toggle='collapse' data-target='#question21' href='#question21' class="pull-left faq__title-content" data-js="panel-header">What is <em>Fitbit</em>?</a>
<a data-toggle='collapse' data-target='#question21' href='#question21' class="hidden-xs faq__title-extend pull-right" data-js="extend-button">+</a>
</h4>
</div>
<div id='question21' class='panel-collapse collapse'>
<div class='panel-body'>
<p>
<em>Fitbit</em> is a wearable gadget which allows you to track your activities (How many steps have you taken? How many floors have you climbed?) and your sleep (How long did you sleep? How often have you awoken at night?). The tracker displays this information on the <em>Fitbit</em> website and calculates how efficient you sleep and many calories you have burned. Their website also allows you to track your body-development (i.e. your weight/BMI) and how much food you have eaten.
</p>
</div>
</div>
</div>
</li>
<li class="faq__single-question ">
<div class='panel panel-default faq__single-question-div'>
<div class='panel-heading faq__single-question-panel'>
<h4 class="panel-title clearfix faq__question-title">
<a data-toggle='collapse' data-target='#question22' href='#question22' class="pull-left faq__title-content" data-js="panel-header">Which data will <em>openSNP</em> save from <em>Fitbit</em>? And will it be public?</a>
<a data-toggle='collapse' data-target='#question22' href='#question22' class="hidden-xs faq__title-extend pull-right" data-js="extend-button">+</a>
</h4>
</div>
<div id='question22' class='panel-collapse collapse'>
<div class='panel-body'>
<p>
If you connect your <em>Fitbit</em> account with <em>openSNP</em> we will save data for up to three different categories: Activities, Body and Sleep. In the activity category we will save your step-count and the number of floors you've climbed for each day. For the body category we will save your Body Mass Index and your weight for each day. In the sleep category we will save how many minutes you have slept, how many minutes you were awake while you tried to sleep, how long it took you to fall asleep and the number of times you awoke for each night.<br/><br/>If you connect your <em>Fitbit</em> account we will also save as much data of past days as it's possible to get through the API of <em>Fitbit</em>. This data is as public as are the genotyping datasets or all standard phenotypes which are entered into openSNP. So please be sure you want the world to be able to see this data.
</p>
</div>
</div>
</div>
</li>
<li class="faq__single-question ">
<div class='panel panel-default faq__single-question-div'>
<div class='panel-heading faq__single-question-panel'>
<h4 class="panel-title clearfix faq__question-title">
<a data-toggle='collapse' data-target='#question23' href='#question23' class="pull-left faq__title-content" data-js="panel-header">Can I choose which data <em>openSNP</em> should save and which not?</a>
<a data-toggle='collapse' data-target='#question23' href='#question23' class="hidden-xs faq__title-extend pull-right" data-js="extend-button">+</a>
</h4>
</div>
<div id='question23' class='panel-collapse collapse'>
<div class='panel-body'>
<p>
Yes, you can choose which for which categories we should save the data. For example you can share our activity-data, so people can see the number of steps you have taken for each day, along with the floors you have climbed, but keep your body and sleep data on <em>FitBit</em>.Two things you should keep in mind: You can (de)activate the saving of data for different categories at any time. <br/><br/>1. If you deactivate a category we will delete all of your data of this category from <em>openSNP</em> (but of course not on <em>Fitbit</em>).<br/>2. If activate a category we will try to save as many data from the past as possible. So if there is past data you don't want to share you probably should not activate it (or delete the data from <em>Fitbit</em>).
</p>
</div>
</div>
</div>
</li>
<li class="faq__single-question ">
<div class='panel panel-default faq__single-question-div'>
<div class='panel-heading faq__single-question-panel'>
<h4 class="panel-title clearfix faq__question-title">
<a data-toggle='collapse' data-target='#question24' href='#question24' class="pull-left faq__title-content" data-js="panel-header">Do you get write-access to my <em>Fitbit</em> account?</a>
<a data-toggle='collapse' data-target='#question24' href='#question24' class="hidden-xs faq__title-extend pull-right" data-js="extend-button">+</a>
</h4>
</div>
<div id='question24' class='panel-collapse collapse'>
<div class='panel-body'>
<p>
No, if you connect your <em>Fitbit</em> account with <em>openSNP</em> we only have read-access. The read-access gives us access to all data you have in your <em>Fitbit</em> account, but we will only read and save the data for which you have given us permission. We're sorry that you have to trust us in this point but the <em>Fitbit</em> API currently doesn't allow for a more granular setting.
</p>
</div>
</div>
</div>
</li>
<li class="faq__single-question ">
<div class='panel panel-default faq__single-question-div'>
<div class='panel-heading faq__single-question-panel'>
<h4 class="panel-title clearfix faq__question-title">
<a data-toggle='collapse' data-target='#question25' href='#question25' class="pull-left faq__title-content" data-js="panel-header">Does <em>openSNP</em> automatically get the latest data from <em>Fitbit</em>?</a>
<a data-toggle='collapse' data-target='#question25' href='#question25' class="hidden-xs faq__title-extend pull-right" data-js="extend-button">+</a>
</h4>
</div>
<div id='question25' class='panel-collapse collapse'>
<div class='panel-body'>
<p>
Yes, once your <em>Fitbit</em> account is connected and set up we will get an notification from <em>Fitbit</em> each time you enter or change any data in their system. So each time your tracker uploads new data to <em>Fitbit</em> we will get those changes as well. This also works for past records. So if you change or delete entries on the <em>Fitbit</em> website those changes will be reflected on <em>openSNP</em> as well.
</p>
</div>
</div>
</div>
</li>
</ul>
</div>

View File

@@ -1,9 +0,0 @@
<p>
the Fitbit data you requested for
<a href="<%="http://"+ActionMailer::Base.default_url_options[:host]+@link%>">
download from openSNP is now available as a CSV (Comma-separated value) file.
</a>
</p>
<p>
Have fun with it!
</p>

View File

@@ -1,4 +0,0 @@
the Fitbit data you requested for download from openSNP is now available as a
CSV (Comma-separated value) file. To download them just visit:
<%="http://"+ActionMailer::Base.default_url_options[:host]+@link%>

View File

@@ -3,9 +3,6 @@
<%= image_tag @user.avatar.url(:thumb), class: "hidden-xs pull-left userpage__profile-picture"%>
<h3>
<%= @user.name %>'s page
<%if @user.fitbit_profile != nil%>
<%=link_to(image_tag("fitbit-icon.png"), {:controller => "fitbit_profiles", :action => "show", :id => @user.fitbit_profile.id}, :data => {toggle:"tooltip", placement:"bottom"}, :title => "See Fitbit data")%>
<%end%>
<%if @user.open_humans_profile != nil%>
<%=link_to(image_tag("open_humans_logo.png",size: '32x32'), {:controller => "open_humans_profiles", :action => "index"}, :data => {toggle:"tooltip", placement:"bottom"}, :title => "Linked to an Open Humans Account")%>
<%end%>

View File

@@ -2,9 +2,6 @@
<div class="col-md-10 col-xs-12 clearfix">
<%= image_tag @user.avatar.url(:thumb), class: "hidden-xs pull-left userpage__profile-picture"%>
<h2>Hello, <%= @first_name %>
<%if @user.fitbit_profile != nil%>
<%=link_to(image_tag("fitbit-icon.png"), {:controller => "fitbit_profiles", :action => "show", :id => @user.fitbit_profile.id}, :data => {toggle:"tooltip", placement:"bottom"}, :title => "See your Fitbit data")%>
<%end%>
<%if @user.open_humans_profile != nil%>
<%=link_to(image_tag("open_humans_logo.png", size: '32x32'), {:controller => "open_humans_profiles", :action => "index"}, :data => {toggle:"tooltip", placement:"bottom"}, :title => "Linked to an Open Humans Account")%>
<%end%>

View File

@@ -1,62 +0,0 @@
# frozen_string_literal: true
class FitbitDump
include Sidekiq::Worker
sidekiq_options queue: :fitbit, retry: 5, unique: true
def perform(fitbit_profile_id,user_id)
fp = FitbitProfile.find_by_id(fitbit_profile_id)
# open handle
@time = Time.now.utc
@time_str = @time.strftime("%Y%m%d%H%M")
@time = @time.to_s.gsub(":","_")
@fitbit_handle = File.new(::Rails.root.to_s+"/public/data/fitbit/user"+fp.user.id.to_s+"_fitbit_data_"+@time_str.to_s+".csv","w")
@fitbit_handle.puts("date;steps;floors;weight;bmi;minutes asleep;minutes awake; times awaken; minutes until fell asleep")
# get all dates which have to be included in the csv
@time_array = []
fp.fitbit_bodies.each do |fb|
@time_array << fb.date_logged
end
fp.fitbit_sleeps.each do |fs|
@time_array << fs.date_logged
end
fp.fitbit_activities.each do |fa|
@time_array << fa.date_logged
end
@time_array = @time_array.uniq.sort
@time_array.each do |d|
@line = d.to_s + ";"
@activity = fp.fitbit_activities.find_by_date_logged(d)
if @activity == nil
@line = @line + "-;-;"
else
@line = @line + @activity.steps.to_s + ";" + @activity.floors.to_s+ ";"
end
@body = fp.fitbit_bodies.find_by_date_logged(d)
if @body == nil
@line = @line + "-;-;"
else
@line = @line + @body.weight.to_s + ";" + @body.bmi.to_s + ";"
end
@sleep = fp.fitbit_sleeps.find_by_date_logged(d)
if @sleep == nil
@line = @line + "-;-;-;-;"
else
@line = @line + @sleep.minutes_asleep.to_s+";"+@sleep.minutes_awake.to_s+";"+@sleep.number_awakenings.to_s+";"+@sleep.minutes_to_sleep.to_s+";"
end
@fitbit_handle.puts(@line)
end
@fitbit_handle.close
puts "Saved fibit-date for "
system("chmod 777 "+::Rails.root.to_s+"/public/data/fitbit/user"+fp.user.id.to_s+"_fitbit_data_"+@time_str.to_s+".csv")
UserMailer.fitbit_dump("/data/fitbit/user#{fp.user.id.to_s}_fitbit_data_#{@time_str.to_s}.csv",user_id).deliver_later
end
end

View File

@@ -47,7 +47,6 @@ class Zipfulldata
logger.info("Starting zipfile #{zip_fs_path}")
Zip::File.open(zip_fs_path, Zip::File::CREATE) do |zipfile|
create_user_csv(genotypes, zipfile)
create_fitbit_csv(zipfile)
list_of_pics = create_picture_phenotype_csv(zipfile)
create_picture_zip(list_of_pics, zipfile)
create_readme(zipfile)
@@ -100,64 +99,6 @@ class Zipfulldata
zipfile.add("phenotypes_#{time_str}.csv", csv_file_name)
end
def create_fitbit_csv(zipfile)
# Create a file of fitbit-data for each user with fitbit-data
fitbit_profiles = FitbitProfile.
includes(:fitbit_activities, :fitbit_bodies, :fitbit_sleeps)
fitbit_profiles.each do |fp|
csv_file_name =
"#{tmp_dir}/dump_user#{fp.user.id}_fitbit_data_#{time_str}.csv"
csv_header = ['date', 'steps', 'floors', 'weight', 'bmi',
'minutes asleep', 'minutes awake', 'times awaken',
'minutes until fell asleep']
CSV.open(csv_file_name, "w", csv_options) do |csv|
csv << csv_header
bodies = fp.fitbit_bodies.group_by(&:date_logged)
sleeps = fp.fitbit_sleeps.group_by(&:date_logged)
activities = fp.fitbit_activities.group_by(&:date_logged)
# get all dates which have to be included in the csv
time_array = []
time_array.concat(bodies.keys)
time_array.concat(sleeps.keys)
time_array.concat(activities.keys)
time_array = time_array.uniq.sort
time_array.each do |d|
row = [d]
activity = activities[d]
if activity.present?
activity = activity.first
row.concat([activity.steps, activity.floors])
else
row.concat(%w(- -))
end
body = bodies[d]
if body.present?
body = body.first
row.concat([body.weight, body.bmi])
else
row.concat(%w(- -))
end
sleep = sleeps[d]
if sleep.present?
sleep = sleep.first
row.concat([sleep.minutes_asleep, sleep.minutes_awake,
sleep.number_awakenings, sleep.minutes_to_sleep])
else
row.concat(%w(- - - -))
end
csv << row
end
end
zipfile.add("user#{fp.user.id}_fitbit_data_#{time_str}.csv", csv_file_name)
logger.info('Saved fibit-date for ')
end
end
# make a CSV describing all of them - which filename is for which user's phenotype
def create_picture_phenotype_csv(zipfile)
file_name = "#{tmp_dir}/picture_dump#{time_str}.csv"

View File

@@ -33,8 +33,6 @@ docker run -d \
-e POSTGRES_URL='postgres://postgres@postgres/postgres' \
-e SECRET_KEY_BASE=foo \
-e SECRET_TOKEN=bar \
-e FITBIT_CONSUMER_KEY=foo \
-e FITBIT_CONSUMER_SECRET=bar \
-e PLOS_API_KEY=foo \
-e RECAPTCHA_PRIVATE_KEY=foo \
-e RECAPTCHA_PUBLIC_KEY=bar \

View File

@@ -39,9 +39,6 @@ Snpr::Application.routes.draw do
get '/openhumans/new', to: 'open_humans_profiles#start_auth'
get '/openhumans/authorize', to: 'open_humans_profiles#authorize', as: :openhumans_authorize
get '/openhumans/destroy/:id', to: 'open_humans_profiles#destroy'
get '/fitbit/show/:id', to: 'fitbit_profiles#show', as: :fitbit_show
get '/fitbit/dump/:id', to: 'fitbit_profiles#dump', as: :fitbit_dump
get '/fitbit/', to: 'fitbit_profiles#index', as: :fitbit_index
get '/phenotypesets/enter/:id', to: 'phenotype_sets#enter_userphenotypes'
get '/phenotypesets/user_phenotypes/save', to: 'phenotype_sets#save_user_phenotypes'
get '/users/:id/changepassword', to: 'users#changepassword'

View File

@@ -10,7 +10,6 @@
- [zipgenotyping,5]
- [zipfulldata,5]
- [recommend,5]
- [fitbit,3]
- [frequency,1]
- [fixphenotypes,1]
- [default, 1]
@@ -29,7 +28,6 @@
recommend: 1
zipgenotyping: 1
zipfulldata: 1
fitbit: 3
frequency: 1
fixphenotypes: 1
mailnewgenotype: 1

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
class RemoveFitbit < ActiveRecord::Migration
def self.up
drop_table :fitbit_activities
drop_table :fitbit_sleeps
drop_table :fitbit_bodies
drop_table :fitbit_profiles
end
def self.down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@@ -272,150 +272,6 @@ CREATE SEQUENCE file_links_id_seq
ALTER SEQUENCE file_links_id_seq OWNED BY file_links.id;
--
-- Name: fitbit_activities; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE fitbit_activities (
id integer NOT NULL,
fitbit_profile_id integer,
steps integer,
floors integer,
date_logged date,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
--
-- Name: fitbit_activities_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE fitbit_activities_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: fitbit_activities_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE fitbit_activities_id_seq OWNED BY fitbit_activities.id;
--
-- Name: fitbit_bodies; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE fitbit_bodies (
id integer NOT NULL,
fitbit_profile_id integer,
date_logged date,
weight double precision,
bmi double precision,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
--
-- Name: fitbit_bodies_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE fitbit_bodies_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: fitbit_bodies_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE fitbit_bodies_id_seq OWNED BY fitbit_bodies.id;
--
-- Name: fitbit_profiles; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE fitbit_profiles (
id integer NOT NULL,
fitbit_user_id character varying(255),
user_id integer,
request_token character varying(255),
request_secret character varying(255),
access_token character varying(255),
access_secret character varying(255),
verifier character varying(255),
body boolean DEFAULT true,
activities boolean DEFAULT true,
sleep boolean DEFAULT true,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
--
-- Name: fitbit_profiles_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE fitbit_profiles_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: fitbit_profiles_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE fitbit_profiles_id_seq OWNED BY fitbit_profiles.id;
--
-- Name: fitbit_sleeps; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE fitbit_sleeps (
id integer NOT NULL,
fitbit_profile_id integer,
minutes_asleep integer,
minutes_awake integer,
number_awakenings integer,
minutes_to_sleep integer,
date_logged date,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
--
-- Name: fitbit_sleeps_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE fitbit_sleeps_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: fitbit_sleeps_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE fitbit_sleeps_id_seq OWNED BY fitbit_sleeps.id;
--
-- Name: friendly_id_slugs; Type: TABLE; Schema: public; Owner: -
--
@@ -1264,34 +1120,6 @@ ALTER TABLE ONLY admin_users ALTER COLUMN id SET DEFAULT nextval('admin_users_id
ALTER TABLE ONLY file_links ALTER COLUMN id SET DEFAULT nextval('file_links_id_seq'::regclass);
--
-- Name: fitbit_activities id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY fitbit_activities ALTER COLUMN id SET DEFAULT nextval('fitbit_activities_id_seq'::regclass);
--
-- Name: fitbit_bodies id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY fitbit_bodies ALTER COLUMN id SET DEFAULT nextval('fitbit_bodies_id_seq'::regclass);
--
-- Name: fitbit_profiles id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY fitbit_profiles ALTER COLUMN id SET DEFAULT nextval('fitbit_profiles_id_seq'::regclass);
--
-- Name: fitbit_sleeps id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY fitbit_sleeps ALTER COLUMN id SET DEFAULT nextval('fitbit_sleeps_id_seq'::regclass);
--
-- Name: friendly_id_slugs id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -1471,38 +1299,6 @@ ALTER TABLE ONLY file_links
ADD CONSTRAINT file_links_pkey PRIMARY KEY (id);
--
-- Name: fitbit_activities fitbit_activities_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY fitbit_activities
ADD CONSTRAINT fitbit_activities_pkey PRIMARY KEY (id);
--
-- Name: fitbit_bodies fitbit_bodies_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY fitbit_bodies
ADD CONSTRAINT fitbit_bodies_pkey PRIMARY KEY (id);
--
-- Name: fitbit_profiles fitbit_profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY fitbit_profiles
ADD CONSTRAINT fitbit_profiles_pkey PRIMARY KEY (id);
--
-- Name: fitbit_sleeps fitbit_sleeps_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY fitbit_sleeps
ADD CONSTRAINT fitbit_sleeps_pkey PRIMARY KEY (id);
--
-- Name: friendly_id_slugs friendly_id_slugs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -1819,14 +1615,6 @@ CREATE UNIQUE INDEX index_users_on_persistence_token ON users USING btree (persi
CREATE INDEX snps_position_idx ON snps USING btree ("position");
--
-- Name: fitbit_profiles fitbit_profiles_user_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY fitbit_profiles
ADD CONSTRAINT fitbit_profiles_user_id_fk FOREIGN KEY (user_id) REFERENCES users(id);
--
-- Name: genotypes genotypes_user_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -2169,3 +1957,5 @@ INSERT INTO schema_migrations (version) VALUES ('20161226175703');
INSERT INTO schema_migrations (version) VALUES ('20171113104813');
INSERT INTO schema_migrations (version) VALUES ('20180118100003');

View File

@@ -1,11 +0,0 @@
# frozen_string_literal: true
RSpec.feature 'Fitbit profiles' do
let!(:user) { create(:user) }
let!(:fitbit_profile) { create(:fitbit_profile, user: user) }
scenario 'are shown' do
visit '/fitbit'
expect(page).to have_content('Listing all connected Fitbit accounts')
end
end

21
spec/requests/das_spec.rb Normal file
View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'DAS-API', type: :request do
let!(:user) { create(:user, name: 'API-Hacker', id: 1) }
let!(:genotype) { create(:genotype,id: 1, user: user) }
let!(:snp) { create(:snp, name: 'rs2345', chromosome: 1, position: 10) }
let!(:snp_two) { create(:snp, name: 'rs1234', chromosome: 1, position: 12) }
let!(:user_snp) { create(:user_snp, user: user, snp: snp, genotype: genotype) }
let!(:user_snp_two) { create(:user_snp, user: user, snp: snp_two, genotype: genotype) }
it 'GET /das/:id/' do
get "/das/#{user.id}/features?segment=1:10,11",
nil,
'SERVER_SOFTWARE' => 'faked for test'
assert_response :success
expect(response.body).to include('rs2345')
expect(response.body).not_to include('rs1234')
end
end

View File

@@ -48,29 +48,6 @@ describe Zipfulldata do
expect(csv).to eq([exp_header, exp_row1, exp_row2])
end
it "creates fitbit CSVs" do
file_name =
"#{job.tmp_dir}/dump_user#{user.id}_fitbit_data_#{job.time_str}.csv"
fp = create(:fitbit_profile, user: user)
expect(zipfile).to receive(:add).
with("user#{fp.user.id}_fitbit_data_#{job.time_str}.csv", file_name)
job.create_fitbit_csv(zipfile)
csv = CSV.read(file_name, job.csv_options)
exp_header = ["date", "steps", "floors", "weight", "bmi",
"minutes asleep", "minutes awake", "times awaken",
"minutes until fell asleep"]
exp_row = [fp.fitbit_activities.first.date_logged.to_s,
fp.fitbit_activities.first.steps.to_s,
fp.fitbit_activities.first.floors.to_s,
fp.fitbit_bodies.first.weight.to_s,
fp.fitbit_bodies.first.bmi.to_s,
fp.fitbit_sleeps.first.minutes_asleep.to_s,
fp.fitbit_sleeps.first.minutes_awake.to_s,
fp.fitbit_sleeps.first.number_awakenings.to_s,
fp.fitbit_sleeps.first.minutes_to_sleep.to_s]
expect(csv).to eq([exp_header, exp_row])
end
it "creates picture phenotype CSVs" do
user2 = create(:user)
pp = create(:picture_phenotype)
@@ -122,7 +99,6 @@ Thanks for using openSNP!
expect(Zip::File).to receive(:open).with(job.zip_fs_path, Zip::File::CREATE).
and_yield(zipfile)
expect(job).to receive(:create_user_csv).with([genotype], zipfile)
expect(job).to receive(:create_fitbit_csv).with(zipfile)
expect(job).to receive(:create_picture_phenotype_csv).with(zipfile).and_return([upp])
expect(job).to receive(:create_picture_zip).with([upp], zipfile)
expect(job).to receive(:create_readme).with(zipfile)

View File

@@ -1,16 +0,0 @@
# frozen_string_literal: true
require_relative '../test_helper'
class FitbitProfilesControllerTest < ActionController::TestCase
context "Fitbit profiles" do
setup do
@user = FactoryGirl.create(:user)
@fitbit_profile = FactoryGirl.create(:fitbit_profile, user: @user)
end
should "show up" do
get 'show', id: @fitbit_profile.id
assert_response :ok
end
end
end