mirror of
https://github.com/fspc/BikeShed-1.git
synced 2026-09-16 16:31:38 -04:00
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53c32a357b | ||
|
|
1191f4448f | ||
|
|
ff3ec4f443 | ||
|
|
98a51f51d2 | ||
|
|
bc6802e5fb | ||
|
|
e48924a228 | ||
|
|
1887840507 | ||
|
|
0629972dca | ||
|
|
6daff093eb | ||
|
|
43faa79f60 | ||
|
|
9e282eda7c | ||
|
|
02fba62499 | ||
|
|
4fe8865aca | ||
|
|
fd5d4798b0 | ||
|
|
3e480981c7 | ||
|
|
523135ed9f | ||
|
|
5e95ce542c | ||
|
|
a8e0c87d37 | ||
|
|
1b2c0fa378 | ||
|
|
9231c91e6c | ||
|
|
e19b80faec |
@@ -6,7 +6,5 @@ tmp/
|
||||
.powrc
|
||||
.rbenv-version
|
||||
.DS_Store
|
||||
public/extjs
|
||||
public/assets
|
||||
public/images/icons
|
||||
config/database.yml
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
ruby-1.9.3-p374
|
||||
ruby-2.1.1
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
FROM ruby:2.1
|
||||
|
||||
# throw errors if Gemfile has been modified since Gemfile.lock
|
||||
RUN bundle config --global frozen 1
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
RUN apt-get update && apt-get install -y nodejs --no-install-recommends && \
|
||||
apt-get install -y mysql-client postgresql-client sqlite3 --no-install-recommends && \
|
||||
apt-get install -y zip unzip --no-install-recommends && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY Gemfile /usr/src/app/
|
||||
COPY Gemfile.lock /usr/src/app/
|
||||
|
||||
RUN bundle install
|
||||
|
||||
COPY install_extjs.rb /usr/src/app/
|
||||
RUN /usr/src/app/install_extjs.rb
|
||||
|
||||
COPY install_famfamfam.rb /usr/src/app/
|
||||
RUN /usr/src/app/install_famfamfam.rb
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["rails", "server", "-b", "0.0.0.0"]
|
||||
@@ -45,7 +45,6 @@ group :test do
|
||||
gem 'launchy', '~> 2.4.2'
|
||||
gem 'spork', '~> 0.9.2'
|
||||
#guard dependency for Mac OS 10
|
||||
gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~ /darwin/i
|
||||
gem 'guard-spork', '~> 1.5.1'
|
||||
gem 'guard-rspec', '~> 4.2.6'
|
||||
end
|
||||
|
||||
+55
-48
@@ -36,9 +36,11 @@ GEM
|
||||
activesupport (3.2.13)
|
||||
i18n (= 0.6.1)
|
||||
multi_json (~> 1.0)
|
||||
addressable (2.3.5)
|
||||
addressable (2.4.0)
|
||||
arel (3.0.3)
|
||||
bcrypt-ruby (3.1.2)
|
||||
bcrypt (3.1.10)
|
||||
bcrypt-ruby (3.1.5)
|
||||
bcrypt (>= 3.1.3)
|
||||
bootstrap-will_paginate (0.0.10)
|
||||
will_paginate
|
||||
builder (3.0.4)
|
||||
@@ -49,19 +51,17 @@ GEM
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
celluloid (0.15.2)
|
||||
timers (~> 1.1.0)
|
||||
childprocess (0.4.0)
|
||||
childprocess (0.5.9)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
cliver (0.3.2)
|
||||
coderay (1.1.0)
|
||||
coderay (1.1.1)
|
||||
coffee-rails (3.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (~> 3.2.0)
|
||||
coffee-script (2.2.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.7.0)
|
||||
coffee-script-source (1.10.0)
|
||||
commonjs (0.2.7)
|
||||
database_cleaner (1.2.0)
|
||||
decent_exposure (1.0.2)
|
||||
@@ -72,7 +72,7 @@ GEM
|
||||
warden (~> 1.1.1)
|
||||
diff-lcs (1.2.5)
|
||||
erubis (2.7.0)
|
||||
execjs (2.0.2)
|
||||
execjs (2.6.0)
|
||||
factory_girl (2.6.4)
|
||||
activesupport (>= 2.3.9)
|
||||
factory_girl_rails (1.7.0)
|
||||
@@ -80,15 +80,18 @@ GEM
|
||||
railties (>= 3.0.0)
|
||||
faker (1.2.0)
|
||||
i18n (~> 0.5)
|
||||
ffi (1.9.3)
|
||||
formatador (0.2.4)
|
||||
guard (2.4.0)
|
||||
ffi (1.9.10)
|
||||
formatador (0.2.5)
|
||||
guard (2.13.0)
|
||||
formatador (>= 0.2.4)
|
||||
listen (~> 2.1)
|
||||
listen (>= 2.7, <= 4.0)
|
||||
lumberjack (~> 1.0)
|
||||
nenv (~> 0.1)
|
||||
notiffany (~> 0.0)
|
||||
pry (>= 0.9.12)
|
||||
shellany (~> 0.0)
|
||||
thor (>= 0.18.1)
|
||||
guard-rspec (4.2.6)
|
||||
guard-rspec (4.2.10)
|
||||
guard (~> 2.1)
|
||||
rspec (>= 2.14, < 4.0)
|
||||
guard-spork (1.5.1)
|
||||
@@ -103,15 +106,15 @@ GEM
|
||||
railties (>= 3.1, < 4.1)
|
||||
hike (1.2.3)
|
||||
i18n (0.6.1)
|
||||
jbuilder (2.0.3)
|
||||
activesupport (>= 3.0.0)
|
||||
multi_json (>= 1.2.0)
|
||||
jbuilder (2.0.8)
|
||||
activesupport (>= 3.0.0, < 5)
|
||||
multi_json (~> 1.2)
|
||||
journey (1.0.4)
|
||||
jquery-rails (2.3.0)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (1.8.1)
|
||||
launchy (2.4.2)
|
||||
json (1.8.3)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
less (2.2.2)
|
||||
commonjs (~> 0.2.6)
|
||||
@@ -119,18 +122,18 @@ GEM
|
||||
actionpack (>= 3.1)
|
||||
less (~> 2.2.0)
|
||||
libv8 (3.3.10.4)
|
||||
listen (2.4.1)
|
||||
celluloid (>= 0.15.2)
|
||||
listen (3.0.6)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
lumberjack (1.0.4)
|
||||
rb-inotify (>= 0.9.7)
|
||||
lumberjack (1.0.10)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.5.2)
|
||||
multi_json (1.8.4)
|
||||
mini_portile2 (2.0.0)
|
||||
multi_json (1.11.2)
|
||||
nenv (0.3.0)
|
||||
netzke-basepack (0.8.4)
|
||||
netzke-core (~> 0.8.2)
|
||||
netzke-cancan (0.8.2)
|
||||
@@ -139,26 +142,29 @@ GEM
|
||||
netzke-core (0.8.4)
|
||||
execjs
|
||||
uglifier
|
||||
nokogiri (1.6.1)
|
||||
mini_portile (~> 0.5.0)
|
||||
nokogiri (1.6.7.2)
|
||||
mini_portile2 (~> 2.0.0.rc2)
|
||||
notiffany (0.0.8)
|
||||
nenv (~> 0.1)
|
||||
shellany (~> 0.0)
|
||||
orm_adapter (0.0.7)
|
||||
pg (0.17.1)
|
||||
poltergeist (1.5.0)
|
||||
poltergeist (1.5.1)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
multi_json (~> 1.0)
|
||||
websocket-driver (>= 0.2.0)
|
||||
polyglot (0.3.3)
|
||||
polyglot (0.3.5)
|
||||
pry (0.9.12.6)
|
||||
coderay (~> 1.0)
|
||||
method_source (~> 0.8)
|
||||
slop (~> 3.4)
|
||||
rack (1.4.5)
|
||||
rack-cache (1.2)
|
||||
rack (1.4.7)
|
||||
rack-cache (1.6.1)
|
||||
rack (>= 0.4)
|
||||
rack-ssl (1.3.3)
|
||||
rack-ssl (1.3.4)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (3.2.13)
|
||||
actionmailer (= 3.2.13)
|
||||
@@ -175,9 +181,9 @@ GEM
|
||||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
rake (10.1.1)
|
||||
rb-fsevent (0.9.4)
|
||||
rb-inotify (0.9.3)
|
||||
rake (10.5.0)
|
||||
rb-fsevent (0.9.7)
|
||||
rb-inotify (0.9.7)
|
||||
ffi (>= 0.5.0)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
@@ -185,11 +191,11 @@ GEM
|
||||
rspec-core (~> 2.14.0)
|
||||
rspec-expectations (~> 2.14.0)
|
||||
rspec-mocks (~> 2.14.0)
|
||||
rspec-core (2.14.7)
|
||||
rspec-core (2.14.8)
|
||||
rspec-expectations (2.14.5)
|
||||
diff-lcs (>= 1.1.3, < 2.0)
|
||||
rspec-mocks (2.14.5)
|
||||
rspec-rails (2.14.1)
|
||||
rspec-mocks (2.14.6)
|
||||
rspec-rails (2.14.2)
|
||||
actionpack (>= 3.0)
|
||||
activemodel (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
@@ -197,19 +203,19 @@ GEM
|
||||
rspec-core (~> 2.14.0)
|
||||
rspec-expectations (~> 2.14.0)
|
||||
rspec-mocks (~> 2.14.0)
|
||||
shellany (0.0.1)
|
||||
shoulda-matchers (1.0.0)
|
||||
slop (3.4.7)
|
||||
slop (3.6.0)
|
||||
spork (0.9.2)
|
||||
sprockets (2.2.2)
|
||||
sprockets (2.2.3)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
therubyracer (0.10.2)
|
||||
libv8 (~> 3.3.10)
|
||||
thor (0.18.1)
|
||||
thor (0.19.1)
|
||||
tilt (1.4.1)
|
||||
timers (1.1.0)
|
||||
treetop (1.4.15)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
@@ -218,14 +224,16 @@ GEM
|
||||
less-rails (~> 2.2.2)
|
||||
railties (>= 3.1)
|
||||
therubyracer (~> 0.10.1)
|
||||
tzinfo (0.3.38)
|
||||
uglifier (2.4.0)
|
||||
tzinfo (0.3.46)
|
||||
uglifier (2.7.2)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
warden (1.1.1)
|
||||
rack (>= 1.0)
|
||||
websocket-driver (0.3.2)
|
||||
will_paginate (3.0.5)
|
||||
websocket-driver (0.6.3)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.2)
|
||||
will_paginate (3.0.7)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
@@ -256,7 +264,6 @@ DEPENDENCIES
|
||||
poltergeist (~> 1.5.0)
|
||||
pry (~> 0.9.8)
|
||||
rails (= 3.2.13)
|
||||
rb-fsevent
|
||||
rspec-rails (~> 2.14.0)
|
||||
shoulda-matchers (~> 1.0.0)
|
||||
spork (~> 0.9.2)
|
||||
|
||||
@@ -1,59 +1,48 @@
|
||||
# About
|
||||
A web application for bicycle collectives to track bicycles, bicycle work history, volunteer hours, volunteer work history, and volunteers currently in the shop.
|
||||
|
||||
See an overview video of what it looks like and how it works here: https://www.youtube.com/watch?v=0-JjM6d9nK4.
|
||||
See a live demo here: <http://bikeshed.wvcompletestreets.org/> Thanks @fspc for setting that up!
|
||||
|
||||
## Instructions/Guides
|
||||
See an overview video of what the desktop view looks like and how it works here: https://www.youtube.com/watch?v=0-JjM6d9nK4.
|
||||
|
||||
### Adding a User
|
||||
To add a user, a user must sign themselves up from the initial login screen. An admin can later log in to grant that user admin privileges afterward, if desired.
|
||||
## Overview/Instructions/Guides
|
||||
|
||||
### Adding a Bike
|
||||
How to add a bike to the database: https://www.youtube.com/watch?v=1IchWUdMQ90.
|
||||
|
||||
## Views
|
||||
The app has two different views. One for "core" volunteers which I consider admins, and another for "customer" volunteers which I consider users. Because of the Netzke/ExtJS framework I used, the current UI is non-intuitive and I feel like is only adequate for admins who can take the time to learn the system. I'm in the process of designing a simpler mobile friendly UI with the essential functionality that's intended for users.
|
||||
|
||||
## Deployment
|
||||
Currently, at the Velocipede collective we have it running on a computer in the shop on the local network. The reason for this, is so that it's only accessible from within the shop. I'm weighing out different methods for ensuring that users can only log in from within the shop (and not from home), such as a shop password that only admins can see and would make visible from within the physical bike shop. I also don't have to worry about security issues while the app is in development. In the future, I'd like to see the app hosted much like http://freehub.bikekitchen.org/.
|
||||
|
||||
## Ideas
|
||||
At Velocipede, with a mobile friendly UI for users, I'm hoping we can get donations of old, unused smart phones, connected via local wifi, that can be used as stations to log a volunteer's work and hours. They can be secured by gluing cables into the earphone jacks to deter theft if necessary. This might be another way that the collective can promote reuse of materials.
|
||||
See [guides](doc/guides.md)
|
||||
|
||||
# Developer Setup
|
||||
|
||||
1. use rvm
|
||||
1. allow the .rvmrc file
|
||||
1. `gem install bundler`
|
||||
1. `bundle`
|
||||
1. Install Postgres (Mac OSX instructions below)
|
||||
1. Copy over DB config: `cp config/database.yml.example config/database.yml`
|
||||
1. Update config with your database (velocipede), user (velocipede), and password.
|
||||
1. `rake db:create db:migrate`
|
||||
1. `rake db:seed`
|
||||
1. Download extJS 4.1 (A version of 4.1 is hosted here: http://my.jasondenney.com/extjs-4.1.1.zip) Latest versions at http://www.sencha.com/products/extjs. Unzip and place where ever you like.
|
||||
1. Link to your extJS folder path under `public/extjs`: (From app root) `ln -s /MY/PATH/extjs/ public/extjs`
|
||||
1. `rails s`
|
||||
## Running with Docker (recommended)
|
||||
|
||||
1. These instructions haven't been tested, please provide corrections!
|
||||
1. Install Docker Toolbox <https://www.docker.com/toolbox>
|
||||
1. Make sure you have a machine running: `docker-machine start default && eval "$(docker-machine env default)"`
|
||||
1. Execute `docker-compose build`
|
||||
1. Execute `docker-compose run web rake db:create db:migrate db:seed`
|
||||
1. Execute `docker-compose up`
|
||||
1. If using Docker Toolbox, use `docker-machine ip default` to get the IP where the server is running.
|
||||
1. Test the Rails server is running with by visiting `<INSERT IP>:8080` in your browser.
|
||||
|
||||
# Postgres 9.2 Mac OSX Install
|
||||
1. Install homebrew `ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"`
|
||||
1. `brew install postgres`
|
||||
1. First time db initialization `initdb /usr/local/var/postgres -E utf8`
|
||||
1. Start Postgres `pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start`
|
||||
1. Create your PG user `createuser -d -P velocipede`
|
||||
1. Create your database `createdb -U velocipede --owner=velocipede velocipede`
|
||||
### Alternative Dockerfile
|
||||
|
||||
# Testing
|
||||
See <https://github.com/fspc/bikeshed> for an alternative docker setup.
|
||||
|
||||
1. Install phantomjs `brew install phantomjs`
|
||||
1. Run tests with `rspec`
|
||||
### Developer Workflow
|
||||
The project directory should already be mounted inside the container, so you should be able to make live changes. However, since the project is running in the 'web' container, you need to prepend commands with `docker-compose run web`.
|
||||
|
||||
# Optional
|
||||
Add icons
|
||||
You'll likely want to add the following aliases:
|
||||
```
|
||||
alias dm='docker-machine'
|
||||
alias dc='docker-compose'
|
||||
alias dcrw='docker-compose run web'
|
||||
```
|
||||
|
||||
1. Download icons from http://www.famfamfam.com/lab/icons/silk/
|
||||
1. Link to the icons under `public/images/icons`: (From app root) `ln -s /MY/PATH/famfamfam_silk_icons/icons public/images/icons`
|
||||
That way your commands can be shortened to:
|
||||
```
|
||||
dcrw rake routes
|
||||
dcrw rails console
|
||||
dcrw rspec
|
||||
```
|
||||
If there's a better way, I'm all ears. Alternatively you could ssh into the machine with `dcrw bash`.
|
||||
|
||||
# License
|
||||
Velocipede is released under the MIT license (http://opensource.org/licenses/MIT)
|
||||
|
||||
@@ -17,3 +17,4 @@
|
||||
//= require utils
|
||||
//= require bootstrap-datepicker
|
||||
//= require bootstrap-timepicker
|
||||
//= require jquery-date-format
|
||||
|
||||
+445
@@ -0,0 +1,445 @@
|
||||
var DateFormat = {};
|
||||
|
||||
(function($) {
|
||||
var daysInWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||
var shortMonthsInYear = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
var longMonthsInYear = ['January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November', 'December'];
|
||||
var shortMonthsToNumber = { 'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06',
|
||||
'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12' };
|
||||
|
||||
var YYYYMMDD_MATCHER = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.?\d{0,3}[Z\-+]?(\d{2}:?\d{2})?/;
|
||||
|
||||
$.format = (function() {
|
||||
function numberToLongDay(value) {
|
||||
// 0 to Sunday
|
||||
// 1 to Monday
|
||||
return daysInWeek[parseInt(value, 10)] || value;
|
||||
}
|
||||
|
||||
function numberToShortMonth(value) {
|
||||
// 1 to Jan
|
||||
// 2 to Feb
|
||||
var monthArrayIndex = parseInt(value, 10) - 1;
|
||||
return shortMonthsInYear[monthArrayIndex] || value;
|
||||
}
|
||||
|
||||
function numberToLongMonth(value) {
|
||||
// 1 to January
|
||||
// 2 to February
|
||||
var monthArrayIndex = parseInt(value, 10) - 1;
|
||||
return longMonthsInYear[monthArrayIndex] || value;
|
||||
}
|
||||
|
||||
function shortMonthToNumber(value) {
|
||||
// Jan to 01
|
||||
// Feb to 02
|
||||
return shortMonthsToNumber[value] || value;
|
||||
}
|
||||
|
||||
function parseTime(value) {
|
||||
// 10:54:50.546
|
||||
// => hour: 10, minute: 54, second: 50, millis: 546
|
||||
// 10:54:50
|
||||
// => hour: 10, minute: 54, second: 50, millis: ''
|
||||
var time = value,
|
||||
values,
|
||||
subValues,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
millis = '',
|
||||
delimited,
|
||||
timeArray;
|
||||
|
||||
if(time.indexOf('.') !== -1) {
|
||||
delimited = time.split('.');
|
||||
// split time and milliseconds
|
||||
time = delimited[0];
|
||||
millis = delimited[1];
|
||||
}
|
||||
|
||||
timeArray = time.split(':');
|
||||
|
||||
if(timeArray.length === 3) {
|
||||
hour = timeArray[0];
|
||||
minute = timeArray[1];
|
||||
// '20 GMT-0200 (BRST)'.replace(/\s.+/, '').replace(/[a-z]/gi, '');
|
||||
// => 20
|
||||
// '20Z'.replace(/\s.+/, '').replace(/[a-z]/gi, '');
|
||||
// => 20
|
||||
second = timeArray[2].replace(/\s.+/, '').replace(/[a-z]/gi, '');
|
||||
// '01:10:20 GMT-0200 (BRST)'.replace(/\s.+/, '').replace(/[a-z]/gi, '');
|
||||
// => 01:10:20
|
||||
// '01:10:20Z'.replace(/\s.+/, '').replace(/[a-z]/gi, '');
|
||||
// => 01:10:20
|
||||
time = time.replace(/\s.+/, '').replace(/[a-z]/gi, '');
|
||||
return {
|
||||
time: time,
|
||||
hour: hour,
|
||||
minute: minute,
|
||||
second: second,
|
||||
millis: millis
|
||||
};
|
||||
}
|
||||
|
||||
return { time : '', hour : '', minute : '', second : '', millis : '' };
|
||||
}
|
||||
|
||||
|
||||
function padding(value, length) {
|
||||
var paddingCount = length - String(value).length;
|
||||
for(var i = 0; i < paddingCount; i++) {
|
||||
value = '0' + value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
parseDate: function(value) {
|
||||
var parsedDate = {
|
||||
date: null,
|
||||
year: null,
|
||||
month: null,
|
||||
dayOfMonth: null,
|
||||
dayOfWeek: null,
|
||||
time: null
|
||||
};
|
||||
|
||||
if(typeof value == 'number') {
|
||||
return this.parseDate(new Date(value));
|
||||
} else if(typeof value.getFullYear == 'function') {
|
||||
parsedDate.year = String(value.getFullYear());
|
||||
// d = new Date(1900, 1, 1) // 1 for Feb instead of Jan.
|
||||
// => Thu Feb 01 1900 00:00:00
|
||||
parsedDate.month = String(value.getMonth() + 1);
|
||||
parsedDate.dayOfMonth = String(value.getDate());
|
||||
parsedDate.time = parseTime(value.toTimeString());
|
||||
} else if(value.search(YYYYMMDD_MATCHER) != -1) {
|
||||
/* 2009-04-19T16:11:05+02:00 || 2009-04-19T16:11:05Z */
|
||||
values = value.split(/[T\+-]/);
|
||||
parsedDate.year = values[0];
|
||||
parsedDate.month = values[1];
|
||||
parsedDate.dayOfMonth = values[2];
|
||||
parsedDate.time = parseTime(values[3].split('.')[0]);
|
||||
} else {
|
||||
values = value.split(' ');
|
||||
switch (values.length) {
|
||||
case 6:
|
||||
/* Wed Jan 13 10:43:41 CET 2010 */
|
||||
parsedDate.year = values[5];
|
||||
parsedDate.month = shortMonthToNumber(values[1]);
|
||||
parsedDate.dayOfMonth = values[2];
|
||||
parsedDate.time = parseTime(values[3]);
|
||||
break;
|
||||
case 2:
|
||||
/* 2009-12-18 10:54:50.546 */
|
||||
subValues = values[0].split('-');
|
||||
parsedDate.year = subValues[0];
|
||||
parsedDate.month = subValues[1];
|
||||
parsedDate.dayOfMonth = subValues[2];
|
||||
parsedDate.time = parseTime(values[1]);
|
||||
break;
|
||||
case 7:
|
||||
/* Tue Mar 01 2011 12:01:42 GMT-0800 (PST) */
|
||||
case 9:
|
||||
/* added by Larry, for Fri Apr 08 2011 00:00:00 GMT+0800 (China Standard Time) */
|
||||
case 10:
|
||||
/* added by Larry, for Fri Apr 08 2011 00:00:00 GMT+0200 (W. Europe Daylight Time) */
|
||||
parsedDate.year = values[3];
|
||||
parsedDate.month = shortMonthToNumber(values[1]);
|
||||
parsedDate.dayOfMonth = values[2];
|
||||
parsedDate.time = parseTime(values[4]);
|
||||
break;
|
||||
case 1:
|
||||
/* added by Jonny, for 2012-02-07CET00:00:00 (Doctrine Entity -> Json Serializer) */
|
||||
subValues = values[0].split('');
|
||||
parsedDate.year = subValues[0] + subValues[1] + subValues[2] + subValues[3];
|
||||
parsedDate.month = subValues[5] + subValues[6];
|
||||
parsedDate.dayOfMonth = subValues[8] + subValues[9];
|
||||
parsedDate.time = parseTime(subValues[13] + subValues[14] + subValues[15] + subValues[16] + subValues[17] + subValues[18] + subValues[19] + subValues[20]);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
parsedDate.date = new Date(parsedDate.year, parsedDate.month - 1, parsedDate.dayOfMonth);
|
||||
parsedDate.dayOfWeek = String(parsedDate.date.getDay());
|
||||
|
||||
return parsedDate;
|
||||
},
|
||||
|
||||
date : function(value, format) {
|
||||
try {
|
||||
var parsedDate = this.parseDate(value);
|
||||
|
||||
if(parsedDate === null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
var date = parsedDate.date,
|
||||
year = parsedDate.year,
|
||||
month = parsedDate.month,
|
||||
dayOfMonth = parsedDate.dayOfMonth,
|
||||
dayOfWeek = parsedDate.dayOfWeek,
|
||||
time = parsedDate.time;
|
||||
|
||||
var pattern = '',
|
||||
retValue = '',
|
||||
unparsedRest = '',
|
||||
inQuote = false;
|
||||
|
||||
/* Issue 1 - variable scope issue in format.date (Thanks jakemonO) */
|
||||
for(var i = 0; i < format.length; i++) {
|
||||
var currentPattern = format.charAt(i);
|
||||
// Look-Ahead Right (LALR)
|
||||
var nextRight = format.charAt(i + 1);
|
||||
|
||||
if (inQuote) {
|
||||
if (currentPattern == "'") {
|
||||
retValue += (pattern === '') ? "'" : pattern;
|
||||
pattern = '';
|
||||
inQuote = false;
|
||||
} else {
|
||||
pattern += currentPattern;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
pattern += currentPattern;
|
||||
unparsedRest = '';
|
||||
switch (pattern) {
|
||||
case 'ddd':
|
||||
retValue += numberToLongDay(dayOfWeek);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'dd':
|
||||
if(nextRight === 'd') {
|
||||
break;
|
||||
}
|
||||
retValue += padding(dayOfMonth, 2);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'd':
|
||||
if(nextRight === 'd') {
|
||||
break;
|
||||
}
|
||||
retValue += parseInt(dayOfMonth, 10);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'D':
|
||||
if(dayOfMonth == 1 || dayOfMonth == 21 || dayOfMonth == 31) {
|
||||
dayOfMonth = parseInt(dayOfMonth, 10) + 'st';
|
||||
} else if(dayOfMonth == 2 || dayOfMonth == 22) {
|
||||
dayOfMonth = parseInt(dayOfMonth, 10) + 'nd';
|
||||
} else if(dayOfMonth == 3 || dayOfMonth == 23) {
|
||||
dayOfMonth = parseInt(dayOfMonth, 10) + 'rd';
|
||||
} else {
|
||||
dayOfMonth = parseInt(dayOfMonth, 10) + 'th';
|
||||
}
|
||||
retValue += dayOfMonth;
|
||||
pattern = '';
|
||||
break;
|
||||
case 'MMMM':
|
||||
retValue += numberToLongMonth(month);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'MMM':
|
||||
if(nextRight === 'M') {
|
||||
break;
|
||||
}
|
||||
retValue += numberToShortMonth(month);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'MM':
|
||||
if(nextRight === 'M') {
|
||||
break;
|
||||
}
|
||||
retValue += padding(month, 2);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'M':
|
||||
if(nextRight === 'M') {
|
||||
break;
|
||||
}
|
||||
retValue += parseInt(month, 10);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'y':
|
||||
case 'yyy':
|
||||
if(nextRight === 'y') {
|
||||
break;
|
||||
}
|
||||
retValue += pattern;
|
||||
pattern = '';
|
||||
break;
|
||||
case 'yy':
|
||||
if(nextRight === 'y') {
|
||||
break;
|
||||
}
|
||||
retValue += String(year).slice(-2);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'yyyy':
|
||||
retValue += year;
|
||||
pattern = '';
|
||||
break;
|
||||
case 'HH':
|
||||
retValue += padding(time.hour, 2);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'H':
|
||||
if(nextRight === 'H') {
|
||||
break;
|
||||
}
|
||||
retValue += parseInt(time.hour, 10);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'hh':
|
||||
/* time.hour is '00' as string == is used instead of === */
|
||||
hour = (parseInt(time.hour, 10) === 0 ? 12 : time.hour < 13 ? time.hour
|
||||
: time.hour - 12);
|
||||
retValue += padding(hour, 2);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'h':
|
||||
if(nextRight === 'h') {
|
||||
break;
|
||||
}
|
||||
hour = (parseInt(time.hour, 10) === 0 ? 12 : time.hour < 13 ? time.hour
|
||||
: time.hour - 12);
|
||||
retValue += parseInt(hour, 10);
|
||||
// Fixing issue https://github.com/phstc/jquery-dateFormat/issues/21
|
||||
// retValue = parseInt(retValue, 10);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'mm':
|
||||
retValue += padding(time.minute, 2);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'm':
|
||||
if(nextRight === 'm') {
|
||||
break;
|
||||
}
|
||||
retValue += time.minute;
|
||||
pattern = '';
|
||||
break;
|
||||
case 'ss':
|
||||
/* ensure only seconds are added to the return string */
|
||||
retValue += padding(time.second.substring(0, 2), 2);
|
||||
pattern = '';
|
||||
break;
|
||||
case 's':
|
||||
if(nextRight === 's') {
|
||||
break;
|
||||
}
|
||||
retValue += time.second;
|
||||
pattern = '';
|
||||
break;
|
||||
case 'S':
|
||||
case 'SS':
|
||||
if(nextRight === 'S') {
|
||||
break;
|
||||
}
|
||||
retValue += pattern;
|
||||
pattern = '';
|
||||
break;
|
||||
case 'SSS':
|
||||
retValue += time.millis.substring(0, 3);
|
||||
pattern = '';
|
||||
break;
|
||||
case 'a':
|
||||
retValue += time.hour >= 12 ? 'PM' : 'AM';
|
||||
pattern = '';
|
||||
break;
|
||||
case 'p':
|
||||
retValue += time.hour >= 12 ? 'p.m.' : 'a.m.';
|
||||
pattern = '';
|
||||
break;
|
||||
case "'":
|
||||
pattern = '';
|
||||
inQuote = true;
|
||||
break;
|
||||
default:
|
||||
retValue += currentPattern;
|
||||
pattern = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
retValue += unparsedRest;
|
||||
return retValue;
|
||||
} catch (e) {
|
||||
if(console && console.log) {
|
||||
console.log(e);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
/*
|
||||
* JavaScript Pretty Date
|
||||
* Copyright (c) 2011 John Resig (ejohn.org)
|
||||
* Licensed under the MIT and GPL licenses.
|
||||
*
|
||||
* Takes an ISO time and returns a string representing how long ago the date
|
||||
* represents
|
||||
*
|
||||
* ('2008-01-28T20:24:17Z') // => '2 hours ago'
|
||||
* ('2008-01-27T22:24:17Z') // => 'Yesterday'
|
||||
* ('2008-01-26T22:24:17Z') // => '2 days ago'
|
||||
* ('2008-01-14T22:24:17Z') // => '2 weeks ago'
|
||||
* ('2007-12-15T22:24:17Z') // => 'more than 5 weeks ago'
|
||||
*
|
||||
*/
|
||||
prettyDate : function(time) {
|
||||
var date;
|
||||
var diff;
|
||||
var day_diff;
|
||||
|
||||
if(typeof time === 'string' || typeof time === 'number') {
|
||||
date = new Date(time);
|
||||
}
|
||||
|
||||
if(typeof time === 'object') {
|
||||
date = new Date(time.toString());
|
||||
}
|
||||
|
||||
diff = (((new Date()).getTime() - date.getTime()) / 1000);
|
||||
|
||||
day_diff = Math.floor(diff / 86400);
|
||||
|
||||
if(isNaN(day_diff) || day_diff < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(diff < 60) {
|
||||
return 'just now';
|
||||
} else if(diff < 120) {
|
||||
return '1 minute ago';
|
||||
} else if(diff < 3600) {
|
||||
return Math.floor(diff / 60) + ' minutes ago';
|
||||
} else if(diff < 7200) {
|
||||
return '1 hour ago';
|
||||
} else if(diff < 86400) {
|
||||
return Math.floor(diff / 3600) + ' hours ago';
|
||||
} else if(day_diff === 1) {
|
||||
return 'Yesterday';
|
||||
} else if(day_diff < 7) {
|
||||
return day_diff + ' days ago';
|
||||
} else if(day_diff < 31) {
|
||||
return Math.ceil(day_diff / 7) + ' weeks ago';
|
||||
} else if(day_diff >= 31) {
|
||||
return 'more than 5 weeks ago';
|
||||
}
|
||||
},
|
||||
toBrowserTimeZone : function(value, format) {
|
||||
return this.date(new Date(value), format || 'MM/dd/yyyy HH:mm:ss');
|
||||
}
|
||||
};
|
||||
}());
|
||||
}(DateFormat));
|
||||
;// require dateFormat.js
|
||||
// please check `dist/jquery.dateFormat.js` for a complete version
|
||||
(function($) {
|
||||
$.format = DateFormat.format;
|
||||
}(jQuery));
|
||||
@@ -1,6 +1,6 @@
|
||||
//when signed out, or session expires forward to sign in page
|
||||
Ext.Ajax.on('requestexception', function(conn, response, options) {
|
||||
if (response.status === 401) { window.location = '/users/sign_in'; }
|
||||
if (response.status === 401 && response.statusText === "Unauthorized") { window.location = '/users/sign_in'; }
|
||||
}, this);
|
||||
|
||||
//Override default netzke time entry field
|
||||
@@ -22,7 +22,6 @@ Ext.define('Ext.ux.form.field.DateTime', {
|
||||
dateSubmitFormat: 'Y-m-d',
|
||||
timeFormat: 'H:i:s',
|
||||
timeSubmitFormat: 'H:i:s',
|
||||
submitFormat: 'c',
|
||||
dateConfig:{},
|
||||
timeConfig:{},
|
||||
|
||||
@@ -185,10 +184,8 @@ Ext.define('Ext.ux.form.field.DateTime', {
|
||||
var me = this
|
||||
,format = me.getFormat()
|
||||
,value = me.getValue();
|
||||
console.log(value);
|
||||
console.log(format);
|
||||
console.log("formatted: " + Ext.Date.format(value, this.submitFormat));
|
||||
return value ? Ext.Date.format(value, this.submitFormat) : null;
|
||||
|
||||
return value ? Ext.Date.format(value, format) : null;
|
||||
},
|
||||
|
||||
setValue: function(value){
|
||||
|
||||
@@ -23,9 +23,13 @@ $(document).ready(function(){
|
||||
forward = "/task_lists/" + bike_id + "/edit";
|
||||
}
|
||||
|
||||
//FIXME: Ideally, we'd submit the dates as ISO, but I can't figure out
|
||||
// how to get Netzke to render UTC dates correctly (it calls to_json
|
||||
// somewhere and drops off the timezone). For the time being, save dates
|
||||
// in locale like Netzke.
|
||||
json_data = { time_entries: [{
|
||||
start_date: start_date.toISOString(),
|
||||
end_date: end_date.toISOString(),
|
||||
start_date: $.format.date(start_date, "dd-MM-yyyy hh:mm a"),
|
||||
end_date: $.format.date(end_date, "dd-MM-yyyy hh:mm a"),
|
||||
log_action_id: parseInt($('input[name=action_id]:checked').val()),
|
||||
bike_id: bike_id,
|
||||
description: $("#description_id").val(),
|
||||
@@ -44,6 +48,7 @@ $(document).ready(function(){
|
||||
displayFormErrors(data.responseJSON);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$(".work_entry-delete-btn").click(function(){
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
});
|
||||
},
|
||||
onChangeAccountInfo: function(){
|
||||
window.location.href="users/edit";
|
||||
window.location.href="/users/edit";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class Ability
|
||||
|
||||
def user
|
||||
can :read, :all
|
||||
can :manage, Bike, :id => @current_user.bike_id unless @current_user.bike.nil?
|
||||
can :manage, @current_user.bike unless @current_user.bike.nil?
|
||||
can :manage, ::ActsAsLoggable::Log, :loggable_type => "Bike", :loggable_id => @current_user.bike_id
|
||||
can :manage, ::ActsAsLoggable::Log, :loggable_type => "User", :loggable_id => @current_user.id
|
||||
end
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
%a{class: "btn btn-lg btn-block btn-primary", href: new_time_entry_path} Add Time Entry
|
||||
%p
|
||||
%a{class: "btn btn-lg btn-block btn-primary", href: time_entries_path} View Timesheet
|
||||
- if can? :manage, Bike
|
||||
%p
|
||||
%a{class: "btn btn-lg btn-block btn-primary", href: new_bike_path} Add Bike
|
||||
|
||||
@@ -13,9 +14,8 @@
|
||||
%p
|
||||
%a{class: "btn btn-lg btn-block btn-primary", href: bike_path(@bike)} View Your Bike
|
||||
|
||||
|
||||
%p
|
||||
%p
|
||||
%a{class: "btn btn-lg btn-block btn-primary hidden-xs", href: admin_index_path} Admin View
|
||||
%a{class: "btn btn-lg btn-block btn-primary hidden-xs", href: admin_index_path} Desktop View
|
||||
%p
|
||||
%input{id: "index_logout", value: "Logout", type: "button", class: "btn btn-lg btn-block btn-danger", "data-url" => destroy_user_session_path }
|
||||
|
||||
+10
-14
@@ -1,23 +1,19 @@
|
||||
development:
|
||||
development: &default
|
||||
adapter: postgresql
|
||||
database: velocipede
|
||||
username: velocipede
|
||||
password:
|
||||
host: 127.0.0.1
|
||||
encoding: unicode
|
||||
pool: 5
|
||||
database: <%= ENV['RDS_DB_NAME'] || 'postgres' %>
|
||||
username: <%= ENV['RDS_USERNAME'] || 'postgres' %>
|
||||
password: <%= ENV['RDS_PASSWORD'] %>
|
||||
host: <%= ENV['RDS_HOSTNAME'] || 'db' %>
|
||||
|
||||
# Warning: The database defined as "test" will be erased and
|
||||
# re-generated from your development database when you run "rake".
|
||||
# Do not set this db to the same as development or production.
|
||||
test:
|
||||
adapter: postgresql
|
||||
<<: *default
|
||||
database: velocipede_test
|
||||
username: velocipede
|
||||
password:
|
||||
host: 127.0.0.1
|
||||
|
||||
production:
|
||||
adapter: postgresql
|
||||
database: velocipede
|
||||
username: velocipede
|
||||
password:
|
||||
host: 127.0.0.1
|
||||
<<: *default
|
||||
database: velocipede_production
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
class AddTimezonesToLogs < ActiveRecord::Migration
|
||||
def up
|
||||
change_table :logs do |t|
|
||||
t.change :start_date, :timestamptz
|
||||
t.change :end_date, :timestamptz
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
change_table :logs do |t|
|
||||
t.change :start_date, :timestamp
|
||||
t.change :end_date, :timestamp
|
||||
end
|
||||
end
|
||||
end
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20140504154355) do
|
||||
ActiveRecord::Schema.define(:version => 20131019170248) do
|
||||
|
||||
create_table "bike_actions", :force => true do |t|
|
||||
t.string "action", :limit => 128, :null => false
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# No API yet
|
||||
@@ -0,0 +1,20 @@
|
||||
# Overview
|
||||
|
||||
## Guides
|
||||
|
||||
### Adding a User
|
||||
To add a user, a user must sign themselves up from the initial login screen. An admin can later log in to grant that user admin privileges afterward, if desired.
|
||||
|
||||
### Adding a Bike
|
||||
How to add a bike to the database via the desktop view: <https://www.youtube.com/watch?v=1IchWUdMQ90>.
|
||||
|
||||
## Views
|
||||
The app has two different views. One for "core" volunteers which I consider admins, and another for "customer" volunteers which I consider users. Because of the Netzke/ExtJS framework I used, the current UI is non-intuitive and I feel like is only adequate for admins who can take the time to learn the system. I'm in the process of designing a simpler mobile friendly UI with the essential functionality that's intended for users.
|
||||
|
||||
## Deployment
|
||||
Currently, at the Velocipede collective we have it running on a computer in the shop on the local network. The reason for this, is so that it's only accessible from within the shop. I'm weighing out different methods for ensuring that users can only log in from within the shop (and not from home), such as a shop password that only admins can see and would make visible from within the physical bike shop. I also don't have to worry about security issues while the app is in development. In the future, I'd like to see the app hosted much like <http://freehub.bikekitchen.org/>.
|
||||
|
||||
## Ideas
|
||||
At Velocipede, with a mobile friendly UI for users, I'm hoping we can get donations of old, unused smart phones, connected via local wifi, that can be used as stations to log a volunteer's work and hours. They can be secured by gluing cables into the earphone jacks to deter theft if necessary. This might be another way that the collective can promote reuse of materials.
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Local Developer Setup
|
||||
|
||||
1. use rvm
|
||||
1. allow the .rvmrc file
|
||||
1. `gem install bundler`
|
||||
1. `bundle`
|
||||
1. Install Postgres (Mac OSX instructions below)
|
||||
1. Copy over DB config: `cp config/database.yml.example config/database.yml`
|
||||
1. Update config with your database (velocipede), user (velocipede), and password.
|
||||
1. `rake db:create db:migrate`
|
||||
1. `rake db:seed`
|
||||
1. Download extJS 4.1 (A version of 4.1 is hosted here: http://my.jasondenney.com/extjs-4.1.1.zip) Latest versions at http://www.sencha.com/products/extjs. Unzip and place where ever you like.
|
||||
1. Link to your extJS folder path under `public/extjs`: (From app root) `ln -s /MY/PATH/extjs/ public/extjs`
|
||||
1. `rails s`
|
||||
|
||||
|
||||
# Postgres 9.2 Mac OSX Install
|
||||
1. Install homebrew `ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"`
|
||||
1. `brew install postgres`
|
||||
1. First time db initialization `initdb /usr/local/var/postgres -E utf8`
|
||||
1. Start Postgres `pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start`
|
||||
1. Create your PG user `createuser -d -P velocipede`
|
||||
1. Create your database `createdb -U velocipede --owner=velocipede velocipede`
|
||||
1. Create your test database `createdb -U velocipede --owner=velocipede velocipede_test`
|
||||
|
||||
# Testing
|
||||
|
||||
1. Install phantomjs `brew install phantomjs`
|
||||
1. Run tests with `rspec`
|
||||
|
||||
# Optional
|
||||
Add icons
|
||||
|
||||
1. Download icons from http://www.famfamfam.com/lab/icons/silk/
|
||||
1. Link to the icons under `public/images/icons`: (From app root) `ln -s /MY/PATH/famfamfam_silk_icons/icons public/images/icons`
|
||||
@@ -0,0 +1,12 @@
|
||||
db:
|
||||
image: postgres
|
||||
ports:
|
||||
- "5432"
|
||||
web:
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
build: .
|
||||
ports:
|
||||
- "8080:3000"
|
||||
links:
|
||||
- db
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'fileutils'
|
||||
|
||||
extjs_url = "http://my.jasondenney.com/extjs-4.1.1.zip"
|
||||
download_dir = '/tmp'
|
||||
dest_dir ='/usr/lib'
|
||||
extjs_download_path = File.join(download_dir,'extjs-4.1.1.zip')
|
||||
|
||||
files = Dir.glob(File.join(dest_dir, 'extjs', '*'))
|
||||
if files.empty? and Dir.glob(extjs_download_path).empty?
|
||||
`wget -P #{download_dir} #{extjs_url}`
|
||||
raise "Failed downloading #{extjs_url}" if Dir.glob(extjs_download_path).empty?
|
||||
end
|
||||
|
||||
if files.empty?
|
||||
puts `unzip #{extjs_download_path} -d #{dest_dir}/`
|
||||
FileUtils.mv(File.join(dest_dir, 'ext-4.1.1a'), File.join(dest_dir, 'extjs'))
|
||||
end
|
||||
raise "Failed unzipping #{extjs_download_path}" if Dir.glob(File.join(dest_dir,'extjs', '*')).empty?
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'fileutils'
|
||||
|
||||
fam_url = "http://www.famfamfam.com/lab/icons/silk/famfamfam_silk_icons_v013.zip"
|
||||
download_dir = '/tmp'
|
||||
dest_dir = "/usr/lib/famfamfam"
|
||||
fam_download_path = File.join(download_dir,'famfamfam_silk_icons_v013.zip')
|
||||
|
||||
files = Dir.glob(File.join(dest_dir, '*'))
|
||||
if files.empty? and Dir.glob(fam_download_path).empty?
|
||||
`wget -P #{download_dir} #{fam_url}`
|
||||
raise "Failed downloading #{fam_url}" if Dir.glob(fam_download_path).empty?
|
||||
end
|
||||
|
||||
if files.empty?
|
||||
puts `unzip #{fam_download_path} -d #{dest_dir}/`
|
||||
end
|
||||
raise "Failed unzipping #{fam_download_path}" if Dir.glob(File.join(dest_dir, '*')).empty?
|
||||
@@ -0,0 +1,109 @@
|
||||
namespace :import do
|
||||
desc "Import a CSV list of bikes. Call via 'rake import:bikes_csv['myFilename.csv']'"
|
||||
task :bikes_csv, [:filename] => :environment do |t, args|
|
||||
require 'csv'
|
||||
# Expected Headers:
|
||||
# Shop Specific ID Number, Program Type/Purpose, Gone (Is it in the shop?),Date In,Date Out,Price/Value,Make/Brand,Model,for Whom,for Whom Zip Code,Comment
|
||||
#
|
||||
# 2,Shop,Yes,Missing,2/17/2008,$90,Univega,Novo Sport Chronology,Darrell Forbes,Missing,,bikes for the World,
|
||||
@loggable_type = 'Bike'
|
||||
@log_action_type= 'ActsAsLoggable::BikeAction'
|
||||
@logger_type = 'User'
|
||||
@logger_id = User.find_by_username('admin')
|
||||
@acquired_log_action_id = ActsAsLoggable::BikeAction.find_by_action('ACQUIRED')
|
||||
@note_log_action_id = ActsAsLoggable::BikeAction.find_by_action('NOTE')
|
||||
@completed_log_action_id = ActsAsLoggable::BikeAction.find_by_action('COMPLETED')
|
||||
@sold_log_action_id = ActsAsLoggable::BikeAction.find_by_action('SOLD')
|
||||
|
||||
@build_bike_matchers = ["build-a-bike"]
|
||||
@sale_matchers = ["as-is","shop"]
|
||||
@parts_matchers = ["parts"]
|
||||
|
||||
def create_acquired_log(bike_id, date)
|
||||
ActsAsLoggable::Log.new(
|
||||
loggable_type: @loggable_type,
|
||||
loggable_id: bike_id,
|
||||
logger_type: @logger_type
|
||||
logger_id: @logger_id
|
||||
log_action_type: @log_action_type
|
||||
log_aciton_id: @acquired_log_aciton_id,
|
||||
start_date: date,
|
||||
end_date: date,
|
||||
)
|
||||
end
|
||||
|
||||
def create_note_log(bike_id, date, note)
|
||||
ActsAsLoggable::Log.new(
|
||||
loggable_type: @loggable_type,
|
||||
loggable_id: bike_id,
|
||||
logger_type: @logger_type
|
||||
logger_id: @logger_id
|
||||
log_action_type: @log_action_type
|
||||
log_aciton_id: @note_log_action_id,
|
||||
start_date: date,
|
||||
end_date: date,
|
||||
description: note,
|
||||
)
|
||||
end
|
||||
|
||||
def create_sold_log(bike_id, date)
|
||||
ActsAsLoggable::Log.new(
|
||||
loggable_type: @loggable_type,
|
||||
loggable_id: bike_id,
|
||||
logger_type: @logger_type
|
||||
logger_id: @logger_id
|
||||
log_action_type: @log_action_type
|
||||
log_aciton_id: @sold_log_action_id,
|
||||
start_date: date,
|
||||
end_date: date,
|
||||
)
|
||||
end
|
||||
|
||||
def create_completed_log(bike_id, date)
|
||||
ActsAsLoggable::Log.new(
|
||||
loggable_type: @loggable_type,
|
||||
loggable_id: bike_id,
|
||||
logger_type: @logger_type
|
||||
logger_id: @logger_id
|
||||
log_action_type: @log_action_type
|
||||
log_aciton_id: @completed_log_action_id,
|
||||
start_date: date,
|
||||
end_date: date,
|
||||
)
|
||||
end
|
||||
|
||||
def get_bike_purpose_id(purpose)
|
||||
case purpose.downcase
|
||||
when *@build_bike_matchers
|
||||
when *@sale_matchers
|
||||
when *@parts_matchers
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
CSV.foreach(args[:filename], headers: true) do | row |
|
||||
bike_id = row["Shop Specific ID Number"]
|
||||
bike_purpose_id = get_bike_purpose_id(row["Program Type/Purpose"])
|
||||
bike_value = get_bike_value(row["Price/Value"])
|
||||
bike_brand_id = get_bike_brand(row["Make/Brand"])
|
||||
bike_model = get_bike_model(row["Model"])
|
||||
|
||||
bike = Bike.find_or_create_by_shop_id_and_bike_purpose_id_and_value_and_bike_brand_id_and_model(
|
||||
bike_id,
|
||||
bike_purpose_id,
|
||||
bike_value,
|
||||
bike_brand_id,
|
||||
bike_model
|
||||
)
|
||||
|
||||
#Create aquired log
|
||||
#
|
||||
#Create For whom log
|
||||
#
|
||||
#Create note/comment log
|
||||
#
|
||||
#Create SOLD/completed log
|
||||
#
|
||||
end
|
||||
end
|
||||
end
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
/usr/lib/extjs
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
/usr/lib/famfamfam/icons/
|
||||
@@ -18,7 +18,7 @@ FactoryGirl.define do
|
||||
bike_purpose { FactoryGirl.create(:bike_purpose) }
|
||||
end
|
||||
|
||||
factory :seed_bike do
|
||||
factory :seed_bike, class: Bike do
|
||||
sequence(:shop_id) {|n| n}
|
||||
sequence :serial_number do |n|
|
||||
"#{Faker::Code.isbn}-#{n}"
|
||||
|
||||
Reference in New Issue
Block a user