using sass @mixins for flexible icons

I’ve been using @extend in my css to create icon classes where it’s easy to change the icon, pretty basic stuff.

.save-icon {
  @extend .fas;
  @extend .fa-save;
  color: $danger;
  &:hover {
    color: lighten($danger, 30%);
  }
}

Then I wanted to the icon to have a label and align it with the icon, I needed a container css class wrapping the icon and label.

.icon-container {
  display: inline-block;
  text-align: center;
  vertical-align: middle;
  margin-left: 20px;
  .icon {
  }
  .icon-label {
    display: block;
  }
  font-size: 120%;
  &.small {
    font-size: 65%;
  }
  &.medium {
    font-size: 75%;
  }
}
.save-label {
  @extend .icon-label;
  color: $danger;
}
<a href....>
  <div class="save-icon">
    <span class="save-icon"></span>
    <span class="save-label">Save</span>
  </div>
</a>

For every icon this requires:

  • Two classes, icon and label
  • Label content in the html, instead of css
  • Each class specifies the colors and hover actions
  • #toomuch …

For me this is too much code and maintenance. Updates touch more code and requires more testing, enter @mixins.

The @mixin below gives us the flexibility to specify the font, label content and color all while implementing features to icons that should be consistent.

@mixin icon-cont($icon_class, $icon_label, $icon-color) {
  display: inline-block;
  text-align: center;
  vertical-align: middle;
  margin-left: 20px;
  font-size: 120%;
  .icon, .label {
    color: $icon-color;
    :hover > & {
      color: lighten($icon-color, 30%);
    }
  }
  .icon {
    @extend .fas;
    @extend #{$icon_class};
  }
  .label {
    display: block;
    &:after {
      content: $icon_label;
      text-transform: uppercase;
    }
  }
  &.small {
    font-size: 65%;
  }
  &.medium {
    font-size: 75%;
  }
  &.selected {
    border-bottom: 3px solid $icon-color;
    &:hover {
      border-bottom: 3px solid lighten($icon-color, 30%);
    }
  }
}

The two other benefits are we not need one css class for each icon/label and they are “one liners”.

.save-icon {
  @include icon-cont('.fa-save', 'save', $danger);
}

Oh yeh the html is simpler.

<a href....>
  <div class="save-icon">
    <span class="icon"></span>
    <span class="label">Save</span>
  </div>
</a>

Not only is this less code overall it’s easy to add new functionality. Notice, the @mixin has &.selected, this is to emphasis what was selected.

&.selected {
  border-bottom: 3px solid $icon-color;
}

Below we are looking at the request statuses and it’s current state, accepted. Furthermore we could easily add a background color to indicate an icon was selected or disabled.

vue.js – mixins

In the middle of a django/vue.js project so I’m learning a ton!

I’m coming across situations where the vue.js component needed some additional logic and  I wanted that logic to be used by multiple components.  In this case it was deciding what css class means data is public vs private.

The DRS API is returning a list of objects that have an attribute ‘public’ which, is either true or false. I didn’t want to add view logic into an API and didn’t want to add the same method to every component that needed it, enter mixins.

project layout

vue_layout

src/mixins/css_mixin.js

vue_mixin_css_mixin

I’m keeping it simple and not showing  how I use sass to abstract the use of font-awesome. Instead I’m using the classes directly.

src/components/SystemDetail.vue

Notice the mixin about specifies a method is being added by putting the code under ‘methods’.

vue_mixin_component

django – Implicit OneToMany with ForeignKey

My experience with Django, so far, has been great. I’m pretty impressed with the Django docs.

Coming from the rails and java world one thing that has been confusing was the missing OneToMany relationships. I’m used to specifying both sides of an association. With Django the missing OneToMany association is actually implicit and re-name-able.

Links below explain this but let’s look at the highlights.

class Blog(models.Model):
  name = models.CharField(max_length=100)
  tagline = models.TextField()

class Entry(models.Model):
  blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
  headline = models.CharField(max_length=255)

In rails and java, I would expect to have a OneToMany in the Blog model pointing “back” to the Entry model.  Instead, django automatically adds this for you as “related objects”, implemented as descriptors.

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.

# b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()

If you want to avoid adding “_all” then you can re-name this with specifying related_name to the ForeignKey definition.

class Entry(models.Model): 
  blog = models.ForeignKey(Blog, ..., related_name='entries') 
  headline = models.CharField(max_length=255)

Now, instead of b.entry_set.all(), you would use b.entries.all().

>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.

Thanks, hope this helps!

REFERENCES

django – searching with ajax

In this post I’ll describe how to create an ajax search page using Django

I’ve really liked using  SGJR – “server generated javascript responses” in rails and wanted something as easy in Django.

Here’s the pattern we’re developing

  • CLIENT: load html page with minimal content
  • CLIENT: once page is loaded: make and ajax request, showing spinner
  • SERVER: perform query from search parameters
  • SERVER: return a html table from the data
  • CLIENT: replace the html in a div with that returned
  • CLIENT: stop spinner

model


class System(models.Model):
system_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=200)
comments = models.TextField()
# ASSOCIATIONS
site = models.ForeignKey(Site,
on_delete=models.SET_NULL,
null=True)

search form


<form id='system_search_form' action="{% url 'metadata:_systems_search' %}" method="post" accept-charset="utf-8">
{% csrf_token %}
<div class="row">
<div class="col-12">
<h3>SYSTEMS SEARCH</h3>
</div>
<div class="col">
<label>Name</label><br />
<input name="name" type="input" class="form-control" />
</div>
<div class="col">
<label>Comments</label><br />
<input name="comments" type="input" class="form-control" />
</div>
<div class="col-2 push-right">
<label class="">Search</label><br />
<input id='system_search_form_btn' class='btn btn-primary' type="submit" />
</div>
</div>
</form>

initial table div


<div id="systems_search_table">
<div class="alert alert-info text-center" role="alert">
Click search to find Systems.
</div>
</div>

the form to submit search criteria

A few things to note:

  • beforeSend() – perform any pre-request items., i’m starting a full-page css only spinner.
  • always() – stopping that spinner regardless of request result
  • done() – this is where the magic happens by replacing the html contents of the div with response, also html.
  • $(input#system_search_form_btn).click(); – this is to submit the form after the page loads. This results is a fast initial page load since your only loading a static page.


<script>
$(document).ready(function() {
$('input#system_search_form_btn').on('click', function(e) {
// submits related form
});
$('form#system_search_form').on('submit', function(e) {
e.preventDefault();
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: $(this).serialize(),
beforeSend: function( xhr ) {
$('#loading').addClass('loading');
}
}).done(function(response) {
$( '#systems_search_table' ).html( response );
}).fail(function(response) {
}).always(function() {
$('#loading').removeClass('loading');
});
});
$('input#system_search_form_btn').click();
});
</script>

view that receives ajax request

I’ve left out pagination, it’s coming next. Basically, this uses the inputs from the form and if there are no results uses a generic template saying something to the affect of “there are no search results, try different search criteria”


@csrf_protect
def systems_search(request):
name = request.POST['name']
comments = request.POST['comments']
systems = System.objects.filter(name__icontains=name, comments__icontains=comments)[:10]
# systems = System.objects.all()[:5]
template = 'no_search_results.html'
context = None
if systems.count() > 0:
template = 'systems/_search.html'
context = {
"systems": systems
}
# https://docs.djangoproject.com/en/2.1/ref/template-response/
return render(request, template, context)

template rendered in ajax response


{% block content %}
<table class="table table-striped table-bordered table-condensed table-hover">
<thead>
<tr>
<th class="">Id</th>
<th class="">Name</th>
<th class="">Site</th>
</thead>
<tbody>
{% for system in systems %}
<tr>
<td>{{ system.system_id }}</td>
<td>{{ system.name }}</td>
<td>{{ system.site.site_name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}

 

django, postgres & redshift – multiple databases

In this post I’ll describe my approach for connecting to multiple databases in a Django app using both PostgresSQL and AWS Redshift.  The approach is basically the same used described in the Django docs “Multiple databases, using routers”

The reason for having multiple databases is the following:

  • Use a Postgres database for tables supporting the Django framework.
  • Use a Redshift database for time-series data and related meta-data.

For example, I didn’t want the tables supporting auth, admin, migrations etc in redshift:

>\dt
                      List of relations
 Schema |            Name            | Type  | 
--------+----------------------------+-------+-
 public | auth_group                 | table | 
 public | auth_group_permissions     | table | 
 public | auth_permission            | table | 
 public | auth_user                  | table | 
 public | auth_user_groups           | table | 
 public | auth_user_user_permissions | table | 
 public | django_admin_log           | table | 
 public | django_content_type        | table | 
 public | django_migrations          | table | 
 public | django_session             | table | 
(10 rows)

Side note, I’m not using Django ORM over the big data tables, only on smaller ‘meta-data’ tables that have a distribution style set to all.

Database configuration – app/settings.py

Since we are using docker I’m storing the environmental variables below in .env at the root of my project.

DATABASES = {
 'default': {
    'ENGINE': 'django.db.backends.postgresql',
    'NAME': os.environ.get('DB_NAME'),
    'USER': os.environ.get('DB_USER'),
    'PASSWORD': os.environ.get('DB_PWD'),
    'HOST': os.environ.get('DB_HOST'),
    'PORT': os.environ.get('DB_PORT'),
  },
  'redshift': {
    'ENGINE': 'django_redshift_backend',
    'NAME': os.environ.get('REDSHIFT_NAME'),
    'USER': os.environ.get('REDSHIFT_USER'),
    'PASSWORD': os.environ.get('REDSHIFT_PWD'),
    'HOST': os.environ.get('REDSHIFT_HOST'),
    'PORT': os.environ.get('REDSHIFT_PORT'),
    # Disabled CURSORS is needed for associations in RedShift
    'DISABLE_SERVER_SIDE_CURSORS': True,
} }

Using app-label

What’s nice about this approach is when you create a new model you only need to modify your models.py file, adding app_label the model’s meta.

Models

The quick read is use Model Meta.app_label as described here https://docs.djangoproject.com/en/1.7/topics/db/multi-db/#an-example

class System(models.Model):
   
    class Meta:
        db_table = 'systems'
        app_label = 'metadata'

Database Router

class RedshiftDBRouter(object):
    def __init__(self):
        """ Initializing a square """
        self.__redshift_tables = ['systems',
                                  'sites',
                                  ]

    def db_for_read(self, model, **hints):
        db = 'default'
        if self.is_redshift(model._meta.app_label):
            db = 'redshift'
        return db

    def db_for_write(self, model, **hints):
        db = 'default'
        if self.is_redshift(model._meta.app_label):
            db = 'redshift'
        return db

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
            Only run migrations for non metadata
        """
        db = None
        if not self.is_redshift(app_label):
            db = 'default'
        return db

    def is_redshift(self, app_label):
        flag = False
        if app_label == 'metadata':
            flag = True
        return flag

 

python, psycopg2.copy_from and postgres

I’ve recently started my journey learning about data science and deep learning techniques. The great thing for me is these disciplines tie my formal education and work experience together nicely: Theoretical Mathematics + Computer Science + Full Stack development. Enough about me, lets get to it.

One of my projects needs a database refactoring into a time-series analytics database. First, I’m moving the existing database to the cloud(Amazon RDS) and changing the schema a bit. I’m taking csv dumps and loading them into a stock PostgreSQL database. From there I’m going to explore different databases and different schemas.

I initially started writing bash scripts to perform the loads, calling postgres \copy command. This approach works but found the ability of bash to query the database lacking. Mainly, when querying with bash there didn’t seem to be a way to map the rows into key-value pairs. Then I realized no self-respecting data scientist would ever use bash and started using python(another thing I’m learning).

My next hurdle was calling the postgres \copy command using psycopg2(postgres python library). The docs and blogs didn’t seem to answer some fundamental questions(or my mastery of skimming docs is lacking):

  • Does psycopg2.copy_from use the COPY(requires access to the database server) or \copy(doesn’t)? I’m assuming it uses \copy since it works from AWS instances connecting to a Postgres RDS instance
  • How do you handle csv files with the first line as he header? (see code below)? Basically, think of reading a file one line at a time the same way you use a cursor when iterating over rows of a database result set.

Loading CSV files with column information using psycopg2.copy_from

    in_file = open(in_file_nm, 'r')
    # FIRST LINE IN FILE ARE COLUMN NAMES
    # TURN FIRST LINE INTO AN ARRAY
    columns = in_file.readline().strip('\n').split(',')
    # LOAD COPY STARTING AT SECOND LINE IN FILE
    cur.copy_from(in_file, 'your_table_name', sep=',', columns=columns)

Hope this helps

rails 4.2 and bootstrap 3.x modal dialog

Creating a modal dialog in rails with bootstrap is pretty easy yet I’ve seen numerous incarnations that seem overly complex. Since it’s the NHL playoffs lets consider showing the details of a player. Here’s a simple example:

controller
app/controllers/players/players_controller.rb

def player_modal
  @player = Player.find_by(id: params[:id])
end

route
config/routes.rb

match '/player_modal/:id', to: 'players#player_modal', via: 'get', as: 'player_modal'

view
app/views/players/players_modal.js.erb

   $('body').append("<%= escape_javascript(render 'players/players_modal', player: @player) %>");
   $('#player-modal-dialog').modal();

The code below clears out modal for subsequent calls

    // must destroy the modal when it is hidden.
    // stackoverflow.com/questions/12286332/twitter-bootstrap-remote-modal-shows-same-content-everytime
    $(document).on("hidden.bs.modal", function (e) {
        $('body').removeClass('modal-open');
        $('.modal-backdrop').remove();
        $('#player-modal-dialog').remove();
    });

modal
app/views/players/_players_modal.html.erb

<!-- standard modal code.... -->
<div class="modal-body">
  name: <%= player.name %>
  positions: <%=player.positions %>
</div>

link

 <%= link_to ({:players, :action => :player_modal,
:id => player.id}, :remote => true) do %>
  <i class='fa fa-something'></i>
<% end %>

rails – mobile first select with ransack and bootstrap

Lets say you have a #rails site and your leveraging #ransack for table searches. Let’s also say your ransack filter has one or more drop-downs with only a few entries that won’t change. And finally, lets assume you want your web app to work well on mobile devices.

Think about how an iPad works with a select:

  1. click select
  2. scroll through options
  3. select the options you want.

Now think about the steps to click a button

  1. click a button

Pretty simple huh?

I recommend getting the select filter working first then changing it.

 

First, add the btn-group to the ransack filter. I also added a hidden field which is what will get sent to the controller.

<div class="col-md-2 col-sm-4 col-xs-6">
  type

  <%= form.hidden_field :point_type_id_eq %>
<div id='point_type' class="btn-group" role="group">
     <button id='point_type-' data-id='' type="button"                class="btn btn-info btn-narrow">
              <i class="fa fa-remove text-md"></i>
     </button>
              <% PointType.all().each do |t| %>
	         <button id='point_type-<%=t.id%>'
                          data-id='<%=t.id%>'
                          type="button"
                          class="btn btn-info">
                   <%= t.code %>
                 </button>
               <% end %></div>
</div>

Now, when a button in the group is selected populate the hidden field and submit the form.

// BUTTON GROUP SELECTION
$("button[id^='point_type-']").on('click', function () {
   if ( $(this).hasClass('active') ) {
    // if its already select no-op
     return
   }
  $('#q_point_type_id_eq').val($(this).data('id'));
  $('#draw_results_form').submit();
});

The controller works as it would with a select and thus the reason I recommended getting select working first.

tbb-ransack-btn-group

Hope this helps your efforts to make mobile first sites.

bootstrap – responsive utilities

I’ve been working on a side project, targetedhunt.com, to visualize hunting statistics and recently pushed it to heroku.

Although I’m using bootstrap I haven’t paid much attention to what the app looked like on phones or tablets. Honestly, I’ve just been too busy with the basics.  Now that I’m showing the app to people I decided it’s time to take advantage of bootstrap’s responsive utilities.

First off, I’m using Chrome’s build-in device emulator to test what the app looks like on various devices.  The device emulator is available by clicking the “Toggle device mode” icon on the upper left of the console.

When I looked at the app on the iphone the data table had too many columns to display. This told me i need to remove and/or combine some columns.

chrome-iphone6-search-page-01

You can also see the emulator tells us where the page’s content ends with the dark gray “shadow” that goes beyond the border of the device’s dimensions.

So now I needed to decided what columns to get rid of. Since you can only search on one species I removed that column but adding hidden-xs css class to the header and data columns.

Most hunters care about success rates and the number of bulls killed so I kept those columns and combined the cows and calves columns. For the cows and calves columns I again used hidden-xs css class. To only show the combined “cow & calf” column we need to hide the column when the device is larger than a phone and hide it when the device is a phone. To accomplish this I used visible-xs-block css class.

&lt;td class=&quot;visible-xs-block&quot;&gt;&lt;%= h.try(:calves)+h.try(:cows) %&gt;&lt;/td&gt;

As you can see this got me pretty close but the table is still a bit wide.

chrome-iphone-search-cowsncalves-toobig

I could have messed with the font size and spacing but this is a side project and I didn’t have enough time. Instead, I removed the “cows & calves” columns.

chrome-iphone6-search-fits

google scripting, oh my!

Recently, I wanted to create a spreadsheet that contained a sub-set of event from a google calendar of mine. When using google calendar I make an effort to name events in a consistent manner so I can look quickly at a reminder and understand whats it’s for. Because my naming convention I figured using an Excel filter to find events in an exported calendar would be easy. I was surprised to find out you can NOT export a google calendar to a csv file.

Luckily, google has a server side javascript framework called Google – App Script. Whats really cool is they supply “apps”, really services, to access other google services(mail, calendar, drive etc).

I thought this looked pretty cool so I decided to try it out.

First, go to script.google.com and you’ll be presented with a simple and effective javascript IDE.

My script does the following:

  1. Gets the Calendar I want to search
  2. Opens the spreadsheet I want to put results in
  3. performs three calendar searches and puts results in a separate tab to the output file

function export_calendar {
//
// FIND THE CALENDAR TO SEARCH
//
var calendars = CalendarApp.getCalendarsByName('calendar-name-to-search');
var calendar = calendars[0];

// DATE RANGE TO SEARCH
var start_date = new Date(2015, 04,01);
var end_date = new Date();

// WHERE TO PUT THE RESULTS
var file = DriveApp.getFilesByName('event-spreadsheet').next();
var spreadsheet = SpreadsheetApp.open(file);
//
// GET EVENTS WITH 'PT' IN IT
// USE THIS SHEET TO HELP VERIGY OTHER SEARCHES
//
pt_events = calendar.getEvents(start_date, end_date, {search: 'PT'});
export_to_spreadsheet(pt_events, spreadsheet, 'pt - all')
//
// GET EVENTS WITH 'PT' AND 'HAND' IN IT
//
pt_events = calendar.getEvents(start_date, end_date, {search: 'PT, HAND'});
export_to_spreadsheet(pt_events, spreadsheet, 'pt - hand')

pt_events = calendar.getEvents(start_date, end_date, {search: 'PT, NECK'});
export_to_spreadsheet(pt_events, spreadsheet, 'pt - neck');

}

Here’s the code that determines in a sheet needs to be added tho the spreadsheet, or if it just needs to be “cleared”, and writes to the google spreadsheet.


function export_to_spreadsheet(events, spreadsheet, sheet_name) {
sheet = spreadsheet.getSheetByName(sheet_name)
if (sheet == null) {
sheet = spreadsheet.insertSheet(sheet_name)
} else {
sheet.clear();
}
sheet.appendRow(['APPOINTMENT', 'DATE', 'DURATION']);
for (var i=0;i&lt;events.length;i++) {
duration_in_min = ( events[i].getEndTime() - events[i].getStartTime())/1000/60
if (duration_in_min &gt; 60 ){
continue;
}
var row=[events[i].getTitle(), events[i].getStartTime().toDateString(), duration_in_min + ' min'];
sheet.appendRow(row);
}

}

Some other features to checkout are triggers & scheduling and deployments(haven’t looked at this but sounds intriguing).