Giter Club home page Giter Club logo

wetrockpolice's Introduction

WetRockPolice

Tests codecov

Table of Contents

Overview

WetRockPolice is an open source project written in Rails 6 + PostgreSQL and hosted on Kubernetes. The project has three key goals:

  • Spread awareness about the ethics of climbing on wet rock at an at-risk area.
  • Display historical rain information to help climbers make informed decisions
  • Enable organizations to connect with visiting climbers

Contibutors are welcome! Please visit the issues tab for ideas on how to contribute.

Running Locally with Docker

  1. Clone the repository

    ~$: git clone [email protected]:Syntaf/wetrockpolice.git
    
  2. Copy over the example environment and fill in any optional integrations

    ~$: cd wetrockpolice
    ~$: cp .env.example .env
    ~$: vim .env
    
  3. Build the containers, and bring them up once finished

    ~$ cd wetrockpolice
    ~$ docker-compose build && docker-compose up -d
    
  4. Run the containers (omit -d if you'd like them to run attached to your shell)

    ~$ docker-compose up -d
    
  5. Run pending migrations

    ~$ docker-compose exec web rails db:migrate
    ~$ docker-compose exec web rails db:migrate RAILS_ENV=test
    
  6. Seed development data

    ~$ docker-compose exec web rails db:seed
    
  7. Visit https://localhost:3001

Contributing

This repository uses Github Actions for continuous integration. Before a pull request can be merged into master it must pass all existing / new tests as well as linting (Rubocop). It is highly recommended that you use Rubcop during development as to not have to go back and forth between CI results and your code.

Using Rubocop on Visual Studio Code:

  • Download the Solargraph Extension and use these settings:

      // settings.json
    
      {
          "solargraph.diagnostics": true,
          "solargraph.formatting": true
      }
    
    

    **Note: If your solargraph server ever crashes and VSCode stops linting your work, use the developer window reload command to restart it.

Using Rubocop from the command line

If you're opposed to VSCode or prefer using a different editor, you can always run rubocop from the command line inside the project:

~$: cd wetrockpolice
~$: rubocop

By default the configuration files should be detected and used, so no arguments are needed.

Coding Standards

For a full manifesto on coding standards, Rubocop will adhere mostly to the Ruby Style Guide. In short, keep these key things in mind:

  • Keep lines under 80 characters
  • Use standard snake_case for varible and method naming
  • Use do end instead of { } when writing multi-line blocks
  • Keep the length of blocks under 25 lines.
  • Don't put business logic inside controllers. Skinny controllers, fat models

While WRP only supports Red Rock currently, the backend is designed to support multiple areas in the future and that should be kept in mind during development. Make sure any changes made consider a future where there are many active areas that can display precipitation data.

Development Guide

Models Overview

The database is designed to support a site that one day may have many watched areas (Red Rock, Moe's Valley, Etc). Below is an Entity Relationship Diagram that can be used to gain an understanding of the models defined in this project.

ERB via LucidChards

LocalClimbingOrg

A not-for-profit climbing coalition managing one or more watched areas. The slug field is used for displaying the coalitions membership signup form within the watched area, i.e. /redrock/sncc where sncc is the slug of the climbing org instance.

WatchedArea

Represents an area being monitored for rain. Uses it's slug field to define a unique "microsite" within WRP consisting of a:

  • Landing page (/redrock)
  • Rainy day options page (/redrock/rainy-day-options)
  • FAQ page (/redrock/faq)
  • Membership signup page (/redrock/sncc)

Fields like info_bubble_excerpt and park_type_word allow for dynamic content on the landing page depending on the current watched area being rendered.

RainyDayArea

Links a climbing area to a watched area. Does not contain any information itself, just serves to define relationships between climbing areas and a watched area.

ClimbingArea

A climbing crag / area. Does not contain any associations to a watched area so that the same climbing area can be a "rainy day option" for multiple watched areas if needed.

Location

Contains a pair of coodinates in the EPSG:3857 WGS 84 / Pseudo-Mercator system, and an optional zoom attribute.

User

A standard devise-based user model. The one unique thing worth mentioning is the manages attribute, which contains an array of watched area IDs. I.e. user.manages == [1] means the user can manage all db data relating to Red Rock.

Frontend Structure

The asset pipeline is kept very simple intentionally. All assets are compressed, loaded and cached on a users first page visit, meaning there are no page specific JS/SCSS files.

JS code is encapsulated into vanilla "controllers"; each controller is responsible for managing the functionality on a given type of page. Since all of these controllers are loaded globally, you can immeditely use them at the bottom of your view template:

<!-- area/rainy_day_options/index.html.erb -->

<div class="rainy-day-content">
  <!-- ... -->
</div>
<script>
    // All controllers are loaded and in global scope
    var pageController = new RainyDayController({ /* ... */ });
</script>

Creating or Updating Hero Images

This repository uses a custom strategy for loading large background images on the client to ensure the first-load experience isn't poor. Without this strategy, the page would load with the large image missing, then slowly load the image top-down as if the page was being printed out.

On page load, the browser will first load and display an extremely small version of the image (20x20 pixels for example) and apply a large gussian blur to distort the pixel borders. In the background, javascript is used to fetch the fully sized background image; once finished the full image will be swapped for the blurred image with a fade-in transition. The end result is a page load that looks like the background image is just being animated into view rather than slowly loaded.

Because of this strategy however, updating landing page images can be a little more involved. Follow these steps for creating or updating hero images:

  1. Create two copies of your desired hero image: One which is the original (high quality) image and another which has had it's size dramatically scaled down (less then 50x50 pixels). Ensure you've maintained the aspect ratio when scaling the image down.

  2. Move your two copies into app/assets/images/watched-area-slug, calling the original image hero-image.jpg and the small image hero-image-small.jpg.

  3. Go to https://www.base64-image.de/ and load your small image to receive a base64 encoded string. Copy that string to your clipboard and open application.html.erb. In the custom style block at the head, add two new SCSS rules:

      /* Watched Area */
      .hero-header.<slug> {
        background: url("data:image/jpeg;base64,/9j/4AAQSkZJ...");
        background-repeat: no-repeat;
        background-position: center center;
        background-size: cover;
      }
      .hero-header.<slug>.hero-header-loaded {
        background-image: url(<%= asset_path 'watchedarea/hero-image.jpg' %>);
      }

    Where <slug> refers to the slug of the watched area, e.g. redrock or castlerock. Since the initial background is a base64 encoded image, the page will almost always load with it already painted (albeit heavily blurred). The second rule will swap the background image while the image has been successfully loaded in the background (avoiding the top-down loading view).

  4. Load the page and ensure your page transitions from the small image to large image. If you're having trouble determing if the small image is loading properly you can comment out imageLoader.load() in views/area/watched_area/index.html.erb, this will cause the page to load with only the small image

Deploying to Kubernetes

Wetrockpolice is hosted on a digital ocean kubernetes cluster, deploying to this cluster (with credentials) is done through Makefile commands and helm. (k9s)[https://k9scli.io/] is a highly recommended tool.

The order of commands should be commit, build, push, the finally deploy

wetrockpolice's People

Contributors

syntaf avatar dependabot[bot] avatar slickmercer avatar

Stargazers

Moksh avatar mlevesquedion avatar Max McDonnell avatar Devon Hirth avatar Elizabeth Ashley avatar Ryan James avatar Sophie Andrews avatar Luke Emery-Fertitta avatar Andrew Reitz avatar Yamm Elnekave avatar Seth Girvan avatar Samuel Singer avatar  avatar Adam Kirosingh avatar Linh Tran avatar nate stemen avatar Jon Wong avatar Justin Lam avatar Aimee Kang avatar Paul DeTrempe avatar Sean Dillon avatar Monica Cowan avatar Anaël Beutot avatar Jacob D. Moorman avatar

Watchers

James Cloos avatar  avatar

wetrockpolice's Issues

Create FAQ model for dynamic FAQ pages

Description

FAQ pages currently display the same static html from views/area/faqs/index.html.erb (Red Rock's FAQs at the moment).

To allow this page to be dynamic a model should be created, Faq using a belongs_to and has_many relationship with a WatchedArea. As for fields, a question and answer field would be expected, both of type string.

To start this issue, you'll run rails g model faq and work from there.

Acceptance Criteria

  • The FAQ page of /redrock/faq is dynamically generated by querying all Faqs belonging to the watched area
  • Tests exist & pass for the FAQ page
  • Some test seed data has been added to seeds.rb for local environments

Docker container fails with "Integrity check failed" on Yarn

Error message for web container after running docker-compose up

web_1  | => Booting Puma
web_1  | => Rails 6.0.2.2 application starting in development
web_1  | => Run `rails server --help` for more startup options
web_1  | warning Integrity check: System parameters don't match
web_1  | error Integrity check failed
web_1  | error Found 1 errors.
web_1  | 
web_1  |
web_1  | ========================================
web_1  |   Your Yarn packages are out of date!
web_1  |   Please run `yarn install --check-files` to update.
web_1  | ========================================
web_1  |
web_1  |
web_1  | To disable this check, please change `check_yarn_integrity`
web_1  | to `false` in your webpacker config file (config/webpacker.yml).
web_1  |
web_1  |
web_1  | yarn check v1.22.4
web_1  | info Visit https://yarnpkg.com/en/docs/cli/check for documentation about this command.
web_1  |
web_1  |
web_1  | Exiting

One option, as the error message states, is to set check_yarn_integrity to false. Ideally a workaround wouldn't be needed, but in this case it might be the only acceptable solution.

Show right side of precipitation graph on mobile

Dom [9:53 AM]
On mobile the precipitation table is default scrolled all the way to the left. Makes more sense to be all the way to the right so you can see the most up to date info first.

Acceptance Criteria:

  • The desktop experience is unchanged
  • The precipitation graph is displayed with the right-most date visible on mobile web

Related Files:

  • views/area/watched_area/index.html.erb (Template)
  • javascripts/landing_controller.js (JS controller)

Create relationship between climbing coalition and membership application

Description

At the moment, membership applications only include a string field referencing the name of the coalition. This should be refactored to instead hold a reference to the climbing coalition itself for better data consistency.

Acceptance Criteria

  • Database migration created to remove string column and add reference column
  • JointMembershipApplication contains a belongs_to association to LocalClimbingOrg
  • Membership signup form refactored to support the above association
  • Tests pass

Create Meta model for supporting dynamic microsites

Description

There are a bit of red rock specific passages in area/watched_area/index.html.erb that need to be refactored out before supporting multiple areas.

This information can be held within a Meta model, belonging to a WatchedArea using the belongs_to and has_one association mappings. The following information would need to be stored inside the Meta model:

  • Geographical term (canyon/area/gorge/etc) for span[data-role="excerpt"]
  • Photographer instagram for the callout at a.plug
  • Educational excerpt (raw html possibly) for the text content in div.text-content .text

Acceptance Criteria

  • area/watched_area/index.html.erb does not contain any red rock specific terminology
  • Text specific to a watched area has been moved into a model fetched alongside the watched area in set_watched_area
  • Tests written
  • Added test data to seeds.rb for local development

Community sourced warning of rain gauge inaccuracy

Background

Wetrockpolice.com relies on a rain gauge located at the Red Rock Visitor center for hourly precipitation observations. Generally, this captures large weather patterns but often misses sporadic showers that might only hit Calico Basic or Black Velvet.

I introduced a "manual warning" on the site some time ago to call out when the rain gauge may be inaccurate, but this relies on me knowing keeping up to date with reports that rain fell in areas not captured by the gauge; if I'm out of town or not checking social media this feature isn't all that helpful

Proposal

Similar to how downdetector.com functions, it would be really cool if we could provide a button somewhere on the page that users can click when they think the rain gauge is inaccurate.

This button would not require any sort of authentication or account, so we'd want to protect the button behind a captcha and write to some sort of events table that can track the timestamp & IP address.

If a certain threshold is reached within a rolling window, a warning can be shown for ~24-48 hours after that threshold time range.

Requirements:

  • A new persistence model to hold events. These events should have a foreign key to a watched area (e.g. Red Rock), an IP address, and a timestamp of the event.
  • A design & implementation of a button somewhere on the landing page which is both clear and not intrusive to the minimalistic style.
  • Logic during the rendering to enable the manual warning when a threshold after parsing recent events, and potentially a blurb about the quantity of events which triggered the manual warning.

Validation fails on membership signups

Steps to reproduce

Description

The following error message is logged on production:

2020-03-24T17:09:51.467866+00:00 app[web.1]: [62650679-953f-4928-b280-8b9ea2ef2b5d] Started POST "/redrock/sncc/validate" for 174.68.138.199 at 2020-03-24 17:09:51 +0000
2020-03-24T17:09:51.469001+00:00 app[web.1]: [62650679-953f-4928-b280-8b9ea2ef2b5d] Processing by Area::MembershipsController#validate as */*
2020-03-24T17:09:51.469144+00:00 app[web.1]: [62650679-953f-4928-b280-8b9ea2ef2b5d]   Parameters: {"authenticity_token"=>"L/9/9NouUYJ2nST8nRvUco8124oZbJmy63+4clgDALpzScpKKAMMREKcFEo/rMLmJwMRaB5Z/h9sweAljXwR6A==", "joint_membership_application"=>{"organization"=>"Southern Nevada Climbers Coalition", "order_id"=>"", "paid_cash"=>"false", "amount_paid"=>"0", "first_name"=>"", "last_name"=>"", "email"=>"", "phone_number"=>"", "street_line_one"=>"", "street_line_two"=>"", "zipcode"=>"", "city"=>"", "state"=>"", "access_fund_shirt"=>"0"}, "slug"=>"redrock", "coalition_slug"=>"sncc"}
2020-03-24T17:09:51.982964+00:00 app[web.1]: [62650679-953f-4928-b280-8b9ea2ef2b5d] Completed 500 Internal Server Error in 514ms (ActiveRecord: 0.0ms | Allocations: 193795)
2020-03-24T17:09:52.150700+00:00 app[web.1]: [62650679-953f-4928-b280-8b9ea2ef2b5d]   
2020-03-24T17:09:52.150704+00:00 app[web.1]: [62650679-953f-4928-b280-8b9ea2ef2b5d] NameError (undefined local variable or method `membership' for #<Area::MembershipsController:0x00005586f72faa70>
2020-03-24T17:09:52.150710+00:00 app[web.1]: Did you mean?  membership_url):
2020-03-24T17:09:52.150710+00:00 app[web.1]: [62650679-953f-4928-b280-8b9ea2ef2b5d]   
2020-03-24T17:09:52.150711+00:00 app[web.1]: [62650679-953f-4928-b280-8b9ea2ef2b5d] app/controllers/area/memberships_controller.rb:43:in `validate'
2020-03-24T17:09:52.156875+00:00 heroku[router]: at=info method=POST path="/redrock/sncc/validate" host=www.wetrockpolice.com request_id=62650679-953f-4928-b280-8b9ea2ef2b5d fwd="174.68.138.199" dyno=web.1 connect=1ms service=691ms status=500 bytes=1891 protocol=https

Acceptance Criteria

  • Validation works
  • A test covering the bug has been written and passes

Proxy Mesowest API calls through backend

Background

Wetrockpolice.com relies on the free tier of the Mesowest API for precipitation observations. Each time a user visits wetrockpolice, we make a call to the Mesowest API for these observations then use javascript to parse out intervals.

Over the last 4 years, the site has organically grown from roughly ~3,000 page views per month during busy season to over 10,000 page views per month today with March 2023 alone generating 20,000 page views.

While this is great news, it does highlight a need to scale the weather API usage before the site is throttled and forced to pay for an enterprise tier.

Implementation

We do know that precipitation observations are generally only updated every 30m or hourly, depending on the weather station being requested. The way I see it we can implement a short term solution followed up by a long term solution

Short Term

Instead of requesting the weather API directly, we can proxy the call behind a WRP api view and apply a micro-cache of 5-10 minutes through a redis instance (which we do have available). This API call can simply forward the data back to the FE.

Long Term

As an improvement of using a micro cache and proxy API, we could configure a cron job that periodically pulls and parses rain interval information to be stored under a new persistence model. This model would represent the result of the parsing and contain easily referenceable information like the last seen rain interval, etc...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.