Just a quick one: I upgraded my Mac OS today to High Sierra only to find that afterwards my installation of nginx did not serve my Django application. Two things needed to be done - the first one was to upgrade my version of homebrew and then to use it to reinstall nginx. Pas de probleme...
The second problem was a bit more tricky to track down. Eventually it turned out that the upgrade process had removed my plist file from the directory /Library/LaunchDaemons. Checking in the install log (over 100K lines, BTW...), it seems that this process is described as 'shoving a sandbox file into quarantine', Cheers, Apple. It would have been nice to know...
I tracked the missing file down to the directory /Volumes/Macintosh HD/Library/SystemMigration/History/Migration-6909D1ED-25C0-43B1-93D7-728FFB8E9108/QuarantineRoot/System/Library/LaunchDaemons/ and had to go through the process of disabling the SIP protection to restore it.
Monday, November 27, 2017
Sunday, March 05, 2017
Using a custom input widget in ag-grid
I am currently working with Angular 2 as my front-end framework, and Django at the back end. I have used ag-grid on a couple of different pages and all of the basic functionality works brilliantly. When straying from the happy path though, things get a little tricky. Here I sketch out the solution I came up with to use a custom cell renderer to provide a drop-down list in a cell, and to pass the selected value back to the underlying data structure.
The application I am building is designed to help with the processing of large comma- or tab-separated datafiles. One of the things I need to do is allow the user to select a function to be applied to some of the data in the file, and what parameters to use. My use case for ag-grid is in the definition of this type of calculation. This screenshot should clarify:
The drop-down lists for parameter and data types are created using an Angular 2 component as a custom cell renderer according to the instructions provided here: https://www.ag-grid.com/best-angular-2-data-grid/#gsc.tab=0
The code for the parameter type cell renderer is shown below.
The key point to notice is the onChange event handler. This fires whenever the selected value of the widget changes. Its purpose is to raise a custom event (wa-parameter-type-changed) which bubbles up through the DOM and is detected in the main document. The event listener is shown below.
The values contained in the event's detail field are used to identify the value that needs to be updated. The grid's rowData is supplied by this.calculation.parameters which are ordered by their sequence numbers, so once the value has been updated by the listener, the state of the data is consistent with the state of the widget.
A further detail is that md-select does not play well with non-string values. I puzzled over this one for a while. When the fields are already populated - for example, when editing a function - it is important that the drop-downs all display the values that are currently set. This is possible using Angular's standard data binding, but only if the Typescript variables are strings. Using numbers will not work. For this reason, I have ended up with string equivalents of the underlying numeric values throughout the application. It's a bit fiddly, but maintaining the consistency all the way through from Django serialisers to the Angular interface widgets minimises the potential for error. For interest, here is an example of an md-select which uses a data-bound variable to specify the selected value:
Background
The application I am building is designed to help with the processing of large comma- or tab-separated datafiles. One of the things I need to do is allow the user to select a function to be applied to some of the data in the file, and what parameters to use. My use case for ag-grid is in the definition of this type of calculation. This screenshot should clarify:
The drop-down lists for parameter and data types are created using an Angular 2 component as a custom cell renderer according to the instructions provided here: https://www.ag-grid.com/best-angular-2-data-grid/#gsc.tab=0
Simple updates
The code for the parameter type cell renderer is shown below.
import {Component} from "@angular/core";
import {AgRendererComponent} from "ag-grid-ng2";
@Component({
moduleId: module.id,
template: `
<md-select placeholder="Parameter type"
[(ngModel)]="selectedValue"
(change)="onChange($event)">
<md-option
ngfor="let parameterType of params.context['parameterType']"
value="{{ parameterType.value }}">
{{ parameterType.name }}
</md-option>
</md-select>
`
})
export class ParameterTypeCellRendererComponent implements AgRendererComponent {
private params: any;
private selectedValue: string;
constructor(){}
agInit(params: any): void {
this.params = params;
this.selectedValue = this.params['data']['stringParameterType'];
}
onChange(event: any) {
let gridEvent: CustomEvent = new CustomEvent('wa-parameter-type-changed', {
'bubbles': true,
'detail': {
'parameterSequence': this.params.data.parameterSequence,
'parameterTypeValue': event.value
}
});
event.source.trigger.nativeElement.dispatchEvent(gridEvent);
}
}
The key point to notice is the onChange event handler. This fires whenever the selected value of the widget changes. Its purpose is to raise a custom event (wa-parameter-type-changed) which bubbles up through the DOM and is detected in the main document. The event listener is shown below.
document.addEventListener('wa-parameter-type-changed', (e) => {
this.calculation.parameters[e['detail']['parameterSequence']].stringParameterType =
e['detail']['parameterTypeValue'];
});
The values contained in the event's detail field are used to identify the value that needs to be updated. The grid's rowData is supplied by this.calculation.parameters which are ordered by their sequence numbers, so once the value has been updated by the listener, the state of the data is consistent with the state of the widget.
Displaying the selected value
A further detail is that md-select does not play well with non-string values. I puzzled over this one for a while. When the fields are already populated - for example, when editing a function - it is important that the drop-downs all display the values that are currently set. This is possible using Angular's standard data binding, but only if the Typescript variables are strings. Using numbers will not work. For this reason, I have ended up with string equivalents of the underlying numeric values throughout the application. It's a bit fiddly, but maintaining the consistency all the way through from Django serialisers to the Angular interface widgets minimises the potential for error. For interest, here is an example of an md-select which uses a data-bound variable to specify the selected value:
<md-select [(ngModel)]="calculation['stringReturnValueDatatype']" placeholder="Return value datatype">
<md-option *ngFor="let datatype of staticData.datatype" value="{{ datatype.value }}">
{{ datatype.name }}
</md-option>
</md-select>
Sunday, February 26, 2017
Automatically starting a Django application on Mac OSX Sierra with nginx and uWSGI
Background
I had to patch this information together from a number of different places, so collecting it all together may provide a useful reference.The background is that I previously had the application running in a Ubuntu VM on a Windows PC. Upgrading to the Mac meant that I could run all desktop applications alongside the server-side apps without context switching, VM resource management issues, etc. However, it did mean that I had to solve a whole bunch of configuration issues.
The Ubuntu setup was fairly straightforward and used an upstart script to start the application. Finding the Mac equivalent was the last part of the process...
Environment
Mac OSX SierraPython 3.5.3 (Anaconda )
Django 1.10.5
Postgres 9.6
nginx 1.10.3
Software installation
This proved to be quite simple on the Mac because there were desktop installers for Postgres and Anaconda Python. Details here:
https://www.enterprisedb.com/downloads/postgres-postgresql-downloads#macosx
https://www.continuum.io/downloads#osx
nginx was installed using Homebrew:
brew install nginx
How easy is that?
More details here:
https://coderwall.com/p/dgwwuq/installing-nginx-in-mac-os-x-maverick-with-homebrew
My application runs in a virtual environment which is set up using conda rather than virtualEnv. Instructions here:
https://conda.io/docs/using/pkgs.html
Activating the virtual environment is a question of running the standard command
source activate
Installing all of the required Python packages (including django and uwsgi) was a question of comparing the new environment with the old one. I did this manually, but in fact a better way would be to use a requirements.txt file. However, some packages can be installed using conda while others have to be installed using pip (not sure why...). I have not checked how this might affect file maintenance using a requirements.txt.
Gotchas
When testing uWSGI with the standard test script, everything seemed to work correctly - no errors in the network log, for example - but no output appeared in the browser window. I eventually realised that this was because I was using Python 3 which requires an indication of the Unicode string type:def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b"Hello!"]
Notice the b prefix on the output string which marks it as a Unicode byte string
Configuring automatic startup
In Unbuntu, nginx is configured to start automatically by default and can be controlled using systemctl. The Django application can be started automatically with an upstart script in the /etc/init.d directory.On the Mac, both processes can be started by creating a plist file in the /System/Library/LaunchDaemons directory. However, this directory is protected by System Integrity Protection (SIP) - details here:
https://support.apple.com/en-gb/HT204899
My approach is to to stay as close to the default system settings as possible so that I can rely on the standard documentation, so although I am familiar with many forms of Unix/Linux which do not use this type of protection, I have kept SIP enabled. It needs to be disabled temporarily though to make the necessary changes to the daemon startup scripts. To do this:
- Boot the Mac into Safe Mode by holding down Command+R until the Apple logo appears
- Select the working language (English)
- From the menu, select Utilities → Terminal
- At the terminal prompt, type
csrutil disable
- Reboot normally
After making the changes below, go through the same process again to re-enable csrutil
plist files
For nginx, follow the instructions here:https://www.nginx.com/resources/wiki/start/topics/examples/osxlaunchd/
For the Django application, use a plist file similar to the one below and enable it in the same way.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/anaconda/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>Label</key> <string>YOUR-SCRIPT-LABEL</string>
<key>Program</key> <string>/anaconda/envs/YOUR-ENV-NAME/app/startup</string>
<key>RunAtLoad</key> <true/>
<key>StandardErrorPath</key> <string>/var/log/SCRIPT-NAME.stderr</string>
<key>StandardOutPath</key> <string>/tmp/SCRIPT_NAME.stdout</string>
<key>WorkingDirectory</key> <string>/anaconda/envs/YOUR-ENV-NAME/app</string>
</dict>
</plist>
- You have created a conda environment whose path is /anaconda/envs/YOUR-ENV-NAME
- You are logging standard and error output to the directory /var/log
The program file startup referenced at line 11 is a shell script which has the following form:
#!/bin/bash env source activate YOUR-ENV-NAME uwsgi --ini wsgi.ini
The problem of setting the python virtual environment is encapsulated by the shell script. In addition, the wsgi parameters are specified in the .ini file.
I found the LaunchControl app very useful in arriving at the final version. Details here:
http://www.soma-zone.com/LaunchControl/
Subscribe to:
Posts (Atom)
