--- layout: post title: 'SEVEN DAYS OF THE NEW MOUNTAIN GOATS ALBUM: DAYS SIX AND SEVEN' date: 2009-10-12 20:49:27.000000000 -07:00 type: post published: true status: publish categories: - SEVEN DAYS OF THE NEW MOUNTAIN GOATS ALBUM tags: - albums are cool - digital versus old school - John Darnielle - SEVEN DAYS OF THE NEW MOUNTAIN GOATS ALBUM - the life of the world to come - the mountain goats meta: _publicize_pending: '1' _edit_last: '6192' aktt_tweeted: '1' original_post_id: '392' _wp_old_slug: '392' author: login: falsefridays email<EMAIL_ADDRESS>display_name: Jim Withington first_name: '' last_name: '' excerpt: | "1 Samuel 15:23," the first track on the Life of the World to Come, exemplifies what we stand to lose in the age of digital music. ---
"1 Samuel 15:23," the first track on the Life of the World to Come, exemplifies what we stand to lose in the age of digital music.
I don't claim this based on the recording itself, though the guitar sound on this track makes me imagine JD on a porch playing this song for three people, when it's not reminding me, somehow, of the Band. Either way, the sound is intimate and menacing and impeccable, three things quite difficult to capture on tape with acoustic instruments.
Really though, I know that without this project, I most likely wouldn't have gotten to know this song. Without dropping it all and deciding to listen to this album--over and over, exclusively--I would have buzz-tracked past it. Lots of track 3, of "1 John," and probably the tracks directly after those (simply by virtue of my commute). Maybe some heavy listening of "Psalms 40:2" because they played it on Colbert.
And the new album would surely have made me listen to the old ones more, if I hadn't committed to solely listening to LOTWTC. It's been especially difficult to avoid hitting up Heretic Pride, especially the title track; sometimes this new album feels like HP's younger brother, the weird one, who shoots frogs when he goes down to the creek on Saturday, and during the week tells his buddies that they are going to hell if they don't go to church on Sunday.
And so if I hadn't been concentrating on the whole album, I would have missed this fantastic, darkly brooding, seething, beauty of a track.
It's easy to rail against "new technology," but then, I got my first iPod in 2003, so "new" wouldn't define it if not for the fact that the music industry it's replacing has been dominant since before I was born. And hey--I wouldn't have picked up the vinyl if not for the fact that I could also get a free download.
Did you know that the BMG Music Service has ceased operations, and now it's basically NetFlix-for-music-but-you-can-buy-stuff? Or that the industry's shitting itself over the fact that vinyl sales are the only increasing segment (and yes, let's wager that that's why we see all these vinyl reissues lousing up your local vinyl emporium)? "If not by faith, but by the sword, [they're] going to be restored?"*
Where do we go with this? Where do I go with this? Do we lament the loss of product simply because albums, complete objects with a certain amount of songs, are somehow the best format? More important than separate tracks? Would faulting people for downloading singles be like faulting orchestras for only playing one of Holst's Planets? Or do we get excited, because more people get to hear more music, get to discover things in a way that's finally not really governed by how much they can afford it?
I don't know. But this experiment I've been doing reminds me of a similar process that English majors go through every time they sign up for a more-than-just-survey-level class. This week has been close reading. And what a text Darnielle wrote.
I suppose his source text gave him a bit of beauty to work with.
* * * * *
Oh, if you're keeping track, I didn't cover Day 6. It was a Sunday. I took a day off.
I'm going to finally go listen to the demos-only version of this album now.
Thanks for hanging.
*"Hebrews 11:40," The Mountain Goats


--- layout: page title: About me subtitle: Name a good movie; chances are I have already seen it! css: "/css/aboutme.css" ---
My name is Saket Kumar.
What else do you need?
I grew up reading Shakespeare's and Wordsworth's, that makes me a firm believer in true love. I believe in simplicity and down-to-earthness. I prefer to do things that bring me inner peace like writing, learning new cultures, travelling, reading, watching movies and of course my job of software's.
My intense addiction for movies make me surf through IMDB for hours. Please feel free to go through my favourite movies list and get in touch to have movie related discussion at any time.
I also have an old blog, that contains my short stories from initial days. I want to have a reference to it somewhere.
You can email me or find me on Twitter if you want to get in touch. As I said, I love meeting new people so don't hesitate to send a message!


You can add nameservers for the domain there (Optional)
Last updated: {{domain.updated_at | date: "medium"}} REMOVE


Detects labels that are never used. Unused labels may signal a mistake or unfinished code. Corresponds to the
unused_labels
rust warning.


In this game you must turn every letter into green by the time you answer the question. Good luck folk!


--- layout: newsstory title: GOOD OLD DAYS match: Yeltz 3 Clevedon 0 ---
Prodigal son Steve Piearce made an excellent return to The Grove with two second half goals in a 3-0 win over Clevedon. With Pie-arce and Jason up front I reckon we'd be a match for anyone in this league, but I'm not sure exactly what Steve's availability is - if anyone knows can they let us know on YeltzTalk ?
We're now 5th in the table and can move up to 3rd with a victory over Taunton at home on Tuesday. It's important we keep our excellent run going if we are going to challenge for the title this year.
After being rightly nagged for not updating it sooner, there's a new vote on the site for your Xmas pleasure.
Halesowen Town: Lowe; Reece, Charlton, Grocutt, Jones, N. Smith (Lowry 78), Hollis (Colwell 78), Ashby, Piearce, Steane, Spencer. Unused Subs: Hallam, Small, Sheils
Clevedon Town: Treago; Arndale, Richards, Baines, Scott, Price, Vickerman, Zabek, Walsh (Weisburg 31), Hobbs (Appleton 31 (Fey 78)), Bale. Unused Subs: A. Smith
Referee: I. Smedley (Derby) Attendance: 386 Booked: None Sponsors' Man of the Match: Simon Hollis
Former favourite Steve Piearce made a dream return to the Grove with two goals in a comfortable Halesowen victory. Piearce notched over fifty goals in two seasons at Halesowen in the late 1990s before moving to Hereford and already looks to have formed a formidable forward partnership with goal-machine Jason Ashby, already on 23 goals for the season.
However, Halesowen started shakily with Darren Grocutt tidying up a goalmouth scramble in the 5th minute with Clevedon's Fidel Richards eventually striking wide. From that point on there was only one winner as Clevedon failed to create any meaningful chances for the rest of the game.
Paul Treago was on top form early on in the Clevedon goal keeping out a fierce 7th minute drive from 15 yards by Jason Ashby after Ben Steane laid off the ball. Piearce and Ashby then combined five minutes later with Ashby flicking the ball off for Piearce to force another fine save from Treago. A Steane shot flashed wide on 13 minutes and Andy Spencer made a long run from midfield in the 14th minute but he hit wide with better options available.
It was no surprise when Dan JONES rose at the far post to squeeze a header into the net from a Simon Hollis corner to give the Yeltz a 26th minute lead. Clevedon then made a double substitution in attack but the move had little effect and it was Halesowen who finished the first half strongly with Neil Smith shooting wide on 42 minutes and Spencer almost latching onto a through ball from Steane just before the break.
Spencer won an early second half corner before Halesowen deservedly increased their lead in the 53rd minute when Steane made a long solo run from midfield and set up PIEARCE to turn on the penalty spot and fire past Treago from 12 yards. Ashby then set up Steane on the hour but he screwed wide from 15 yards out.
In a rare counter-attack, a casual back header from Jones almost let in Clevedon's Ryan Weisburg in the 67th minute but the under-worked goalkeeper Matt Lowe did just enough to distract the substitute striker. With little else on offer from the visitors Hollis blasted over the bar in the 68th minute and then saw his shot blocked on the line five minutes later with Piearce forcing a corner from the rebound. From the corner a wide-angled shot from Hollis beat the Clevedon defence and PIEARCE was on hand to convert his second goal from close range in the 73rd minute. In the final ten minutes Halesowen introduced 18-year youngster Mark Lowry who had only just made the move from Stafford Rangers.


You can compare these numbers in the same ways as the ways you might have chosen to compare 100 and 1000, but they may not be as meaningful or useful. Instead, you might want to see Can you think of any other ways to compare these numbers?
in-context


from IPython.display import display, Math, Latex
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import collections
import cv2 #this needs to be imported before TF!!
import tensorflow as tf
from tensorflow.contrib.layers import flatten
import random
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
from six.moves import map
%matplotlib inline
# Load pickled data
import pickle
training_file = 'traffic-signs-data/train.p'
testing_file = 'traffic-signs-data/test.p'
class_names = pd.read_csv('traffic-signs-data/signnames.csv')
with open(training_file, mode='rb') as f:
train = pickle.load(f)
with open(testing_file, mode='rb') as f:
test = pickle.load(f)
X_train, y_train = train['features'], train['labels']
X_test, y_test = test['features'], test['labels']
Below I show a sample for each class in the dataset. There are 43 unique classes. Some classes (particularly the speed limit signs) are overrepresented in the dataset. We might need to revisit this class imbalance problem later to improve the accuracy. Though this should be somewhat OK with the assumption that the train data and the test data come from the same distribution - e.g class imbalance persist in the test data.
In all, the training data and test data consist of 39209 and 12630 images, respectively. The images are of size 32x32.
n_train = y_train.shape[0]
n_test = y_test.shape[0]
image_shape = X_train.shape[1:]
NB_CLASSES = len(set(y_train))
print("Number of training examples =", n_train)
print("Number of testing examples =", n_test)
print("Image data shape =", image_shape)
print("Number of classes =", NB_CLASSES)
#################
# Parameters #
#################
# A better approach here would be to pass the params as a dictionary into the model to iterate faster on the hyperparameters
base_image_path = "traffic-signs-data/"
IMAGE_SIZE = 32
# number of epochs
NB_EPOCHS = 100
NB_TRAIN_SAMPLES = 50000
# batch size
BATCH_SIZE = 128
checkpoint_name = "model.ckpt"
# filter size
FILTER_SIZE = 5
#number of conv filters
NB_FILTERS = 32
#input channels
INPUT_CHANNELS = 3
# number of hidden layers
NB_HIDDEN = 128
# Learning Rate
LR = 0.001
y = pd.DataFrame(y_train,columns=['label'])
y['value'] = y.index
ix = np.array(y.groupby('label').first())
ix = np.squeeze(ix)
X_sample = X_train[ix,:,:,:]
im_grid = X_sample.shape[0] // 2
plt.figure(figsize=(300,200))
for i in range(NB_CLASSES):
plt.subplot(im_grid,4, i+1)
plt.axis('off')
plt.imshow(X_sample[i], aspect='auto')
_y_dist = collections.Counter(y_train)
y_dist = pd.DataFrame.from_dict(_y_dist, orient='index').reset_index()
y_dist.columns = ['ClassId','Count']
y_dist = y_dist.merge(class_names, on='ClassId').drop('ClassId',axis=1)
y_dist.index = y_dist.SignName
plt.figure(figsize=(200,100))
y_dist.plot.bar()
y_classes = y_dist.index.str.lower().tolist()
y_classes
The pixels are scaled down [0-1] range. Centering the input data around zero helps to allevaite dead or saturated neuron issues. I did not reduce the image to the gray-scale because it turns out that keeping the original three channel helps with the accuracy. This makes sense given that certain traffic signs have distinct colors associated with them.
The idea behind real-time data augmentation is to make the model more resistant to overfitting. By applying small distortions to the dataset, we implicitly force the model to memorize less and generalize better.
What type of augmentions can we use for this problem? Upon inspection of the image classes and fitting the model on images generated through combinations of transformations, it turns out flipping the images upside-down or left-to-right is not such a great idea. Because when you flip a go straight or right
sign, it actually becomes go straight or left
. Smoothing the image did not help either, possibly the image size is very small to begin with.
I have found that randomly rotating the image confined to a certain degree, however, turns out to be a helpful augmentation technique. Distoring the brightness of the image seemed to help slightly with validation accuracy as well.
The code below, ImageGenerator
, acts as a generator by scaling and applying random distortions to the input image. This in turn allows us to run batches that are not confined to the actual number of training examples in the dataset.
The data is split into training and validation, using a 80-20 split.
# Image Generation. Inspired by Keras image generation logic.
class ImageGenerator():
def __init__(self, batch_size, x, y):
self.x = x
self.y = y
self.batch_index = 0
self.batch_size = batch_size
self.shift_degree = 22.5
self.N = len(x)
self.shape = (32, 32)
self.index_generator = self._flow_index(self.N, batch_size, shuffle)
# maintain the state
def _flow_index(self, N, batch_size, shuffle=True):
while 1:
if self.batch_index == 0:
index_array = np.arange(N)
if shuffle:
index_array = np.random.permutation(N)
current_index = (self.batch_index * batch_size) % N
if N >= current_index + batch_size:
current_batch_size = batch_size
self.batch_index += 1
else:
current_batch_size = N - current_index
self.batch_index = 0
yield (index_array[current_index: current_index + current_batch_size],
current_index, current_batch_size)
def __iter__(self):
return self
def __next__(self):
ix_array, current_index, current_batch_size = next(self.index_generator)
batch_x = np.zeros(tuple([current_batch_size] + list(self.x.shape)[1:]))
for i, j in enumerate(ix_array):
x = self.x[j]
x = self.transform(x)
x = self.mean_substract(x)
batch_x[i] = x
batch_y = self.y[ix_array]
return batch_x, batch_y
def transform(self,x):
#x = self.random_flip(x)
#x = self.random_gaussian_blur(x)
x = self.random_brightness(x)
x = self.random_rotate(x)
return x
def random_flip(self, x):
if np.random.random() < 0.5:
x = np.flipud(x)
if np.random.random() < 0.5:
x = np.fliplr(x)
return x
def random_rotate(self,x):
# Rotation of an image for an angle theta is achieved by the transformation matrix of the form M
# Rotate the image by a random magnitude centered around shift_degree
random_degree = np.random.uniform(low=-1, high=1) * self.shift_degree
M = cv2.getRotationMatrix2D((self.shape[0]/2,self.shape[1]/2),random_degree,1)
return cv2.warpAffine(x,M,self.shape)
def random_gaussian_blur(self, img, kernel_size=3):
if np.random.random() < 0.5:
return cv2.GaussianBlur(img, (kernel_size, kernel_size), 0)
else:
return img
def random_brightness(self, img):
image1 = cv2.cvtColor(img,cv2.COLOR_RGB2HSV)
random_bright = np.random.uniform(low=0.7, high=1.5)
image1[:,:,2] = image1[:,:,2] * random_bright
image1 = cv2.cvtColor(image1,cv2.COLOR_HSV2RGB)
return image1
def mean_substract(self,img):
#return img - np.mean(img)
#Scale the features to be [0,1]
return (img / 255.).astype(np.float32)
For completeness, I give examples of each transformation the generator is capable of.
# Image-flippin
_ix = 990
x1 = np.fliplr(X_train[_ix])
x2 = np.flipud(X_train[_ix])
plt.subplot(1, 3, 1)
plt.imshow(X_train[_ix], aspect='auto')
plt.subplot(1, 3, 2)
plt.imshow(x1, aspect='auto')
plt.subplot(1, 3, 3)
plt.imshow(x2, aspect='auto')
# Denoising applies a gaussian filter to smooth out the data
x1 = cv2.GaussianBlur(X_train[_ix], (3, 3), 0)
plt.subplot(1, 2, 1)
plt.imshow(X_train[_ix], aspect='auto')
plt.subplot(1, 2, 2)
plt.imshow(x1, aspect='auto')
# Random Brightness adjustment
_ix = 399
image1 = cv2.cvtColor(X_train[_ix],cv2.COLOR_RGB2HSV)
random_bright = np.random.uniform(low=0, high=2)
print(random_bright)
image1[:,:,2] = image1[:,:,2] * random_bright
image1 = cv2.cvtColor(image1,cv2.COLOR_HSV2RGB)
plt.subplot(1, 2, 1)
plt.imshow(X_train[_ix], aspect='auto')
plt.subplot(1, 2, 2)
plt.imshow(image1, aspect='auto')
#Randomly Rotate Slightly
random_degree = np.random.uniform(low=-1, high=1) * 22.5
M = cv2.getRotationMatrix2D((32/2,32/2),random_degree,1)
image1 = cv2.warpAffine(X_train[_ix],M,(32,32))
plt.subplot(1, 2, 1)
plt.imshow(X_train[_ix], aspect='auto')
plt.subplot(1, 2, 2)
plt.imshow(image1, aspect='auto')
I split the data into training and validation, using a 80-20 split.
train_test_split_ratio = 0.2
# Shuffle and split into validation / training
ix = list(range(n_train))
random.shuffle(ix)
X_train = X_train[ix,:]
y_train = y_train[ix]
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size= train_test_split_ratio , random_state=0)
print("the size of the training and validation data: {}, {}".format(X_train.shape[0], X_val.shape[0]))
NB_TRAIN_DATA = len(y_train)
I make sure that I normalize the image data for the validation and test set.
def mean_substract(img):
#return img - np.mean(img)
return (img / 255.).astype(np.float32)
X_val_normalized = mean_substract(X_val)
X_test_normalized = mean_substract(X_test)
X_val_normalized.shape
I built a 3 layer CNN achitechture with filter size 5x5. AdamOptimizer
combines the logic of AdaGrad
optimizer with momentum. In short, AdaGrad
keeps track of historical sums of squares in each gradient, which is used to normalize the immediate gradient update. Momentum, in turn, allows a velocity to build up - very much like in physics along gradient surfaces.
The batch size is set to 128. A larger batch size is more stable, but takes a longer time to run. I have iterated over synthetic dataset through 100 epochs, which seems to be adaquate enough to get to a decent error / accuracy rate. I could have trained the model through more epochs in combiniation with an early-stopping mechanism. For each epoch, the data generator generates 50,000 images randomly. The initial learning rate is set to 0.001. The hyperparameters are mostly chosen by trial-and-error. One needs to be careful with peeking too much into the validation data however. The dirtier the validation set gets, the less meaningful it is as a proxy for out-of-sample accuracy.
Weights used in the convolutional layers were initialized using a truncated normal distribution with a standard deviation of 0.1. Bias weights were either initialized to zeros or ones. Weights for the fully connected layers were initialized also using a truncated normal distribution with a standard deviation of 0.1.
Image classes were transformed into one-hot encodings. A reduced mean, cross entropy loss function was fed the logits from the last fully connected layer. I experimented with couple of regularization techniques such as L2 or batch normalization or dropout. The latter two did not offer any improvemens over L2 norm on the fully-connected layer, so I did stick with L2 norm.
| Layer | Description |
|---|---|
| Input | 32x32x3 RGB Image - Normalized and Randomly Perturbed |
| Convolution 5x5 | 32 filters. Same padding. 1x1 stride |
| RELU | |
| Max Pooling | 2x2 stride, kernel size of 2x2 |
| Convolution 5x5 | 32 filters. Same padding. 1x1 stride |
| RELU | |
| Max Pooling | 2x2 stride, kernel size of 2x2 |
| Fully Connected Layer | 128 hidden layers |
| Fully Connected Layer | Softmax Layer. 43 classes |
#Reset the graph
tf.reset_default_graph()
graph = tf.get_default_graph()
assert [] == graph.get_operations()
# Models.
def convNet(data, one_hot_y, loss_beta = 0.01):
# Variables.
# For saving and optimizer.
global_step = tf.Variable(0, name="global_step")
# conv weights
layer1_weights = tf.Variable(tf.truncated_normal(
[FILTER_SIZE, FILTER_SIZE, INPUT_CHANNELS, NB_FILTERS], stddev=0.1), name="layer1_weights")
layer1_biases = tf.Variable(tf.zeros([NB_FILTERS]), name="layer1_biases")
layer2_weights = tf.Variable(tf.truncated_normal(
[FILTER_SIZE, FILTER_SIZE, NB_FILTERS, NB_FILTERS], stddev=0.1), name="layer2_weights")
layer2_biases = tf.Variable(tf.constant(1.0, shape=[NB_FILTERS]), name="layer2_biases")
# fully connected layers
layer3_weights = tf.Variable(tf.truncated_normal(
[IMAGE_SIZE // 4 * IMAGE_SIZE // 4 * NB_FILTERS, NB_HIDDEN], stddev=0.1), name="layer3_weights")
l2_layer3 = loss_beta * tf.nn.l2_loss(layer3_weights)
layer3_biases = tf.Variable(tf.constant(1.0, shape=[NB_HIDDEN]), name="layer3_biases")
layer4_weights = tf.Variable(tf.truncated_normal(
[NB_HIDDEN, NB_CLASSES], stddev=0.1), name="layer4_weights")
#l2_layer4 = loss_beta * tf.nn.l2_loss(layer4_weights)
layer4_biases = tf.Variable(tf.constant(1.0, shape=[NB_CLASSES]), name="layer4_biases")
# 1st Convolution
conv = tf.nn.conv2d(data, layer1_weights, [1, 1, 1, 1], padding='SAME')
# Relu activation of conv
hidden = tf.nn.relu(conv + layer1_biases)
# Max pooling of activation
hidden_pooled = tf.nn.max_pool(hidden, ksize = [1,2,2,1], strides = [1,2,2,1], padding='SAME')
#print(hidden_pooled.get_shape())
# 2nd Convolution
conv = tf.nn.conv2d(hidden_pooled, layer2_weights, [1, 1, 1, 1], padding='SAME')
# Relu activation of conv
hidden = tf.nn.relu(conv + layer2_biases)
# Max pooling
hidden_pooled = tf.nn.max_pool(hidden, ksize = [1,2,2,1], strides = [1,2,2,1], padding='SAME')
#print(hidden_pooled.get_shape())
# Flatten
shape = hidden_pooled.get_shape().as_list()
reshape = tf.reshape(hidden_pooled, [-1, shape[1] * shape[2] * shape[3]])
# 1st fully connected layer with relu activation
#print(reshape.get_shape())
hidden = tf.nn.relu(tf.matmul(reshape, layer3_weights) + layer3_biases)
# 2nd fully connected layer
logits = tf.matmul(hidden, layer4_weights) + layer4_biases
# loss
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits, one_hot_y)
loss_operation = tf.reduce_mean(cross_entropy)
loss_operation += l2_layer3
#loss_operation += l2_layer4
# accuracy
correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(one_hot_y, 1))
accuracy_operation = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
return logits, loss_operation, correct_prediction, accuracy_operation
print('done')
# Placeholders.
tf.reset_default_graph()
global_step = tf.Variable(0, name="global_step")
x = tf.placeholder(
tf.float32, shape=(None, IMAGE_SIZE, IMAGE_SIZE, INPUT_CHANNELS),name='x')
y = tf.placeholder(tf.int32, shape=(None),name='y')
one_hot_y = tf.one_hot(y, NB_CLASSES)
# Set the graph computations.
logits, loss_operation, correct_prediction, accuracy_operation = convNet(x, one_hot_y)
# cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits, one_hot_y)
# loss_operation = tf.reduce_mean(cross_entropy)
optimizer = tf.train.AdamOptimizer(learning_rate = LR)
training_operation = optimizer.minimize(loss_operation)
Setting up the final ops and evaluation function for validation accuracy.
saver = tf.train.Saver()
def evaluate(X_data, y_data):
num_examples = len(X_data)
total_accuracy = 0
sess = tf.get_default_session()
for offset in range(0, num_examples, BATCH_SIZE):
batch_x, batch_y = X_data[offset:offset+BATCH_SIZE], y_data[offset:offset+BATCH_SIZE]
accuracy = sess.run(accuracy_operation, feed_dict={x: batch_x, y: batch_y})
total_accuracy += (accuracy * len(batch_x))
return total_accuracy / num_examples
print('done')
gr = tf.get_default_graph()
print("Number of ops in TF Graph is {}".format(len(gr.get_operations())))
This is where we finally train our model.
#Initiate the generator
gen = ImageGenerator(BATCH_SIZE, X_train, y_train)
model_path = base_image_path + checkpoint_name
tr_error_rate = []
vl_accuracy = []
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print("Training...")
print()
for i in range(NB_EPOCHS):
print("Epoch {} ...".format(i+1))
for offset in range(0, NB_TRAIN_SAMPLES, BATCH_SIZE):
batch_x, batch_y = next(gen)
_, l = sess.run([training_operation, loss_operation] , feed_dict={x: batch_x, y: batch_y})
tr_error_rate.append(l)
validation_accuracy = evaluate(X_val_normalized, y_val)
vl_accuracy.append(validation_accuracy)
print("Training Loss = {:.3f}".format(l))
print("Validation Accuracy = {:.3f}".format(validation_accuracy))
print()
if (i % 10 == 0) and (i > 0):
print("Saving the temp chkpt..")
# Save the variables to disk.
save_path = saver.save(sess, model_path)
print("Model saved in file: %s" % model_path)
print()
# Save the variables to disk.
save_path = saver.save(sess, model_path)
print("Final model saved in file: %s" % model_path)
print('done')
# Training error and validation accuracy plot
df = pd.DataFrame(np.array(tr_error_rate),columns=['training_error'])
df2 = pd.DataFrame(np.array(vl_accuracy),columns=['validation_accuracy'])
df.plot()
df2.plot()
Next, I want to see if there are classes where my trained network is doing poorly. This is a bit of an iterative process. The reader only gets to see the final result of many iterations over the parameter hyperspace.
def predict(X_data):
num_examples = len(X_data)
predictions = []
sess = tf.get_default_session()
for offset in range(0, num_examples, BATCH_SIZE):
batch_x = X_data[offset:offset+BATCH_SIZE]
_predictions = sess.run(logits, feed_dict={x: batch_x})
predictions.extend(_predictions)
return np.array(predictions)
print(model_path)
#Test Predictions
with tf.Session() as sess:
saver.restore(sess, model_path)
print("Model Loaded...")
predictions = predict(X_test_normalized)
print('done')
#Create a lookup for labels
class_names = class_names.drop('ClassId',axis=1)
lookup = class_names.to_dict()['SignName']
val_preds = [(lookup[np.argmax(p,0)], lookup[y]) for p,y in zip(predictions, y_test)]
val_preds = pd.DataFrame(val_preds,columns=['pred','truth'])
confusion_matrix = val_preds.groupby(['pred','truth']).size()
confusion_matrix = confusion_matrix.reset_index()
confusion_matrix.columns = val_preds.columns.tolist() + ['cnt']
confusion_matrix = confusion_matrix.pivot(index='pred', columns='truth', values='cnt').fillna(0)
nb_truth = np.array(confusion_matrix.sum(axis=0)).T
correct_pred = np.array(confusion_matrix).diagonal().T
false_pred = (nb_truth - correct_pred).T
false_pred.shape, correct_pred.shape, nb_truth.shape
preds = pd.DataFrame({'ground_truth' : nb_truth, 'correct_pred': correct_pred, 'false_pred': false_pred})
preds.index = confusion_matrix.sum(axis=0).index
preds['accuracy'] = preds['correct_pred'] / preds['ground_truth']
preds.sort_values('accuracy')
# accuracy by class
preds.drop(['ground_truth','accuracy'],axis=1).plot.bar(stacked=True)
# Accuracy by class size
preds.drop(['correct_pred','false_pred'],axis=1).plot(kind='scatter',x='ground_truth',y='accuracy')
Finally, after we are satisfied with all the tweaking, I took the test data out of the box and give it a whirl..
#Test Accuracy
with tf.Session() as sess:
saver.restore(sess, model_path)
print("Model Loaded...")
test_accuracy = evaluate(X_test_normalized, y_test)
print("Test Accuracy for Test Data = {:.3f}".format(test_accuracy))
The validation set accuracy is 0.987, whereas the test set accuracy turned out to be 0.950.
After trying several architechtures including LeNet, I settled with a plain-vanilla ConvNet architecture because it led to a higher accuracy rate out of the box. The hyperparameter space is very very large even for a simple model such as this.
Iterating over different combinations, the trick that helped the most was (i) to scale the data and (ii) augmented data (random rotations) (iii) L2 regulizer.
You can see above that the accuracy in the validation set by class vary. There are 11 classes where the accuracy rate in validation data stays below 90% including Beware of ice/snow
, Double curve
, Pedestrians
- outliers in the lowe left corner in the scatter plot. Further iterations can alleviate this issue by fixing the class imbalance. Secondly, the model seems to confuse Speed limit (80km/h)
with End of speed limit (80km/h)
as both classes have lower accuracy rates.
Third, the fact that validation accuracy is higher than the test accuracy leads me to believe that the model does not generalize as good as it should. Tweaking the regularization parameter and/or adding dropout or normalization layers might alleviate the issue.
Finally, I can try more powerful models. A model with more capacity might be able to learn features that are beyond this little ConvNet's reach.
Now we apply the image classifier on images hat tI have found in the wild. The accuracy rate turns out to be a dismal 25%. This is a cautoniary tale that the model does not necessarily generalize well when released in the wild. I suspect that the model has hard time recognization objects in images that are not head-on or with other objects in the background. The model seems also be fooled by shapes - Speed limit (30km/h)
is mistaken as Roundabout mandatory
sign, both of which lot of circular shapes in common. There are some blatant errors that need a more in-depth analysis. For example, the model predicts that the Stop
sign is a Turn left ahead
sign.
import os
ground_truth = {'speed_limit_120.jpg': 8,
'Schilderwald_in_Passau.jpg': 1,
'186819557.jpg': 13,
'532135995.jpg': 12,
'slippery_road.jpg': 23,
'stop.jpg': 14,
'19.jpg' : 31,
'speed_limit_100.jpg': 7,
}
image_names = os.listdir("street_signs/")
processed_images = []
class_labels = []
for image_name in image_names:
if image_name in ground_truth:
image = cv2.imread("street_signs/"+ image_name)
#print(image.shape)
class_labels.append(ground_truth[image_name])
#preprocessing step. first crop then normalize
resized = cv2.resize(image, (32,32))
#print(resized.shape)
wi = mean_substract(resized)
processed_images.append(wi)
print('*'*50)
print(class_labels)
# predict images in the wild
pred = []
with tf.Session() as sess:
saver.restore(sess, model_path)
print("Model Loaded...")
print("Prediction for {} images".format(len(processed_images)))
_pred = predict(processed_images)
_pred.shape
wild_preds = [(lookup[np.argmax(p,0)], lookup[y]) for p,y in zip(_pred, class_labels)]
wild_preds
_v = np.exp(_pred).sum(axis=1)
softmax_probs = np.round(np.exp(_pred) / _v[:,None],2)
pred_wild = pd.DataFrame(softmax_probs, columns = class_names.SignName, index = class_labels)
for i, (lbl, pred) in enumerate(pred_wild.iterrows()):
_pred = pred.sort_values(ascending=False).head(5)
print(i)
plt.imshow(processed_images[i])
plt.show()
print("Label: {}\n".format(lookup[lbl]))
print("Top 5 predictions:\n")
print(_pred)
print('='*10)
Note: Once you have completed all of the code implementations and successfully answered each question above, you may finalize your work by exporting the iPython Notebook as an HTML document. You can do this by using the menu above and navigating to \n", "File -> Download as -> HTML (.html). Include the finished document along with this notebook as your submission.


The Black Goose Bistro offers casual lunch and dinner fare in a hip atmosphere. The menu changesregularly to highlight the
freshest ingredients.
You have fun... we'll handle the cooking. Black Goose Catering can handle events from snacks for bridge club to elegant
corporate fundraisers.
Seekonk, Massachusetts;
Monday through Thursday 11am to 9pm,
Friday and Saturday, 11am to midnight


--- title: A simple Form Builder using jQueryUI - Part 1 tags: jquery, form builder, javascript meta: jquery, jqueryUI, formbuilder, form builder, javascript, customization abstract: In this post, we will see how to implement jQuery and jQueryUI to create the first part of form builder. In this part, the form builder will be used to place fields in a column layout. thumbnail: formbuilder-p1-thumbnail.png date: 2013-01-26 ---
In this post, we will see how to implement jQuery and jQueryUI to create the first part of form builder. In this part, the form builder will be used to place fields in a column layout – specifically a two column layout.
Here we define a table with 3 columns. The first column contains the list of available elements. The other two columns are empty and act as a customizable two column layout.

  Available elements 	  Drop the fields in one of these columns

  <input type="button" value="Preview" onclick="preview();"></input>

In the first column we define a DIV with draggable elements. The list of elements is generated by a method getFields(..). These values may be coming from a database or from a selected list of implemented elements - For now we use this simple method that returns list of fields that contain the name, label and type of the field.
/* populate the list of fields into the div */ var fields = getFields(); $(fields).each(function(index,value) { $("#listOfFields").append("
"+value.label+"
"); }); $(".fld").draggable({ helper: "clone", stack: "#containerTable div",cursor: "move" });
As shown above, we first create the list of fields with a class "fld". After creating the list, we simply mark that class as draggable. In the draggable options, we mention helper - "clone" to avoid moving the original object. The cursor is set to Move and the stack is set so as to show the fields being dragged above the other elements.
Now that we have made these elements draggable, let's define a drop target for these. This is simply done by the below:
$( ".droppedFields" ).droppable({ activeClass: "activeDroppable", hoverClass: "hoverDroppable", accept: ":not(.ui-sortable-helper)", drop: function( event, ui ) { var fieldtype = $(ui.draggable).attr("fieldtype"); $( "

") .html( ui.draggable.html()).appendTo( this ); } });
We have created two table column DIVs with class "droppedFields". We the above line we mark those as dropppable. In the drop function we create a new DIV element and append it to the column-DIVs.
The "accept" option defines what draggable elements are allowed to be dropped in the column-DIVs. Applying ":not(.ui-sortable-helper)" prevents the drop when sorting the elements within the column-DIV. The elements are made sortable with the following lines:
$( ".droppedFields" ).sortable(); $( ".droppedFields" ).disableSelection();
User can now drag and drop the elements from the list of available elements and place those into one of the two containers.
In this demo, when you click preview, we collect the list of elements in each column and create an array of objects defining the columns. This array is currently used to show the list of columns in the preview - but this can be processed further to render HTML elements. This array can also be serialized and stored into a database to preserve the customization. Keep looking for further enhancements in other parts of this series and happy coding


Introduction
The following procedure will guide you through the creation of a new Eclipse project for your web app. At the end you will have a Web Project that can be run in a Java EE server (e.g. Tomcat) with all the needed layers set up and ready for customization:
-
Database access layer with transactions (Hibernate / JPA / Spring)
-
Web application controller (Spring)
-
Page templates (Thymeleaf)
-
Security (Spring)
You will also have a basic deployment procedure already set up:
-
Database schema generator
-
Configurations for development, test and production
-
Deployment script
Prerequisites
You should have Java, git, MySQL Community Server and a working copy of Eclipse IDE. Check the notes in this section before installing anything.
Java SDK
Remove any Java installation you may have on your PC.
|
Java version mismatch
The biggest problem you may face with Java installation is to mix 32 and 64 bit versions: it gives unexpected results. So always download 64 bit versions of Java and Eclipse. Currently the Yada Framework source is tested with Java 8. Running on an untested version might give weird errors. At the time of writing we failed to run on Java 11.0.6 for problems during Gradle project import. |
You should look on the Oracle site for the "Java SE" installer, which is the "Standard Edition" of Java. You will probably be required to create an Oracle account and login before being able to download. The OpenJDK version of Java should also work fine if you prefer open source software.
|
JDK vs JRE
Before Java 11 there were two components in Java SE: the JDK (Development Kit) and the JRE (Runtime Environment). Since Java 11 the JDK is also a JRE. |
The installation package will probably install both the JDK and the JRE.
The JRE - 64 bit - can be any version and generally it will auto-upgrade to the latest.
To make your installation future-proof, install the java JDK in some folder like C:\Local\Javas\jdk1.8.0_152
and
make a symbolic link to it with a generic name, like
mklink /D /J C:\Local\jdk8 C:\Local\Javas\jdk1.8.0_152
Now you can refer to C:\Local\jdk8
in your scripts and when you update the java (minor) version you just change the link, not the scripts.
The Java JRE can be installed to the default folder (C:\Program Files
).
GIT
Install git for your OS.
MySQL
Install MySQL Community Server v8: any later versions have not been tested yet.
For a future-proof installation you should not install MySQL as a Service, but run it manually when needed because one day you might have more than one version to use.
If you already have a MySQL installation, you may use it. Just check that the "lower_case_table_names" system variable is set to "2": if it’s not, it can’t be changed after installation so you can either reinstall, install a new instance or live with it (see below).
If you don’t have a MySQL installation already, the easiest way to install it is by downloading the "MySQL installer":
-
on the MySQL Download Page click on "MySQL Installer": you will be presented with two downloads, the smaller one is the "web installer" and it’s the one you should use. You will be asked to login or register before download but there’s also a link to download right away.
-
run the installer and choose the version you want to install: be sure to check the "Enable the Select Features page to customize product features" checkbox before continuing (NOTE: not sure it’s needed but do it anyway) because you’ll need to set the "lower_case_table_names" system variable before installation. If you don’t do this on Windows, you either need to uninstall and reinstall the MySQL server or ignore the "lower_case_table_names" setting, in which case you might write SQL queries that don’t work on the production Linux servers
-
when installing you will get to some wizard pages that allow to select the following:
-
"Show Advanced and Logging Options" should be checked
-
authentication method: any would do
-
root password: you may leave it blank on your development computer (do as you wish)
-
install as a service: you should not configure the server as a service if you have many versions on the same port and want to start the one you need manually (recommended approach)
-
"Advanced Options": "Preserve Given Case" should be checked. This will set the "lower_case_table_names" appropriately as explained above
-
After installation, make a symbolic link to the server folder with a generic name, like
mklink /D /J C:\Local\mysql80 C:\local\Mysqls\mysql-8.0.19-winx64
Now you can refer to C:\Local\mysql80
in your scripts and when you want to use a different installation
you just change the link, not the scripts.
In order to start/stop the MySQL server you should create two scripts and put their shortcut somewhere easy like the desktop or the taskbar.
set MYSQL_HOME=C:\Local\mysql80
set PATH=%MYSQL_HOME%\bin;%PATH%
C:\Local\mysql80\bin\mysqld.exe --defaults-file="%MYSQL_HOME%\my.ini" --console
set MYSQL_HOME=C:\Local\mysql80
C:\Local\mysql80\bin\mysqladmin.exe -u root shutdown
It is very convenient to set an empty password for root on the development computer (the above script won’t work otherwise):
mysql -u root -p
SET PASSWORD FOR 'root'@'localhost' = '';
When you start MySQL you may get a few errors and warnings like "Failed to set up SSL" or "Cannot open Windows EventLog": you can safely ignore them as long as the process doesn’t stop.
Eclipse
You can edit the source files and compile the binaries in any IDE. This documentation only shows how to set up Eclipse though. You don’t actually need an IDE to develop a web application, but it can make life a lot easier compared to editing files in a generic text editor and compiling from the command line.
| Eclipse jee 2019-12-R throws a weird "VerifyError: Inconsistent stackmap frames" on some occasions. Don’t use that version. |
Install the latest version of Eclipse IDE for Enterprise Java and Web Developers. Be sure to install the 64 bit "Enterprise Java and Web" version.
|
Need Enterprise version?
A 32 bit Eclipse will not run on a 64 bit Java installation so be careful not to install the 32 bit. The non-enterprise version will not be able to run a web application in a standalone Tomcat, and it lacks some plugins that you would otherwise need to install separately. If you need a minimalistic development environment, you should be able to code with the non-enterprise version though. |
With a single installation of Eclipse you can have as many workspaces as you like. For big projects involving multiple branches and many people, you might want to create at least two workspaces, one for your own development and another one for checking other people’s work or for deployment. It is very convenient to create a different shortcut for each workspace. Using the "-data" command line option you can specify which workspace you want to use. For example:
C:\local\Eclipses\eclipse-jee-2019-09-R\eclipse.exe -data "C:\work\wspaces\myCoding"
C:\local\Eclipses\eclipse-jee-2019-09-R\eclipse.exe -data "C:\work\wspaces\myRelease"
Create a workspace for the bookstore tutorial:
C:\local\Eclipses\eclipse-jee-2019-09-R\eclipse.exe -data "C:\work\wspaces\yadaTutorial"
Run Eclipse with the chosen workspace and click on the "Workbench" or "Hide" icon to the right of the "Welcome to Eclipse" page.
Update your installation with "Help > Check for Updates" and follow the instructions. Failing to do so might prevent you from adding a Tomcat server later.
From the "Window > Preferences > Gradle" dialog set the "Java home" entry to your JDK installation,
which would be C:\Local\jdk8
if you followed the instructions above. This is very important. Failing to do so may result in a "java facet" mismatch error on compilation.
Set the editors file encoding to UTF-8, at least "General > Workspace > Text file encoding" and "Web > CSS Files > Encoding":
The "New text file line delimiter" shown in the above image is also better set to "Unix".
Also set UTF-8 in the "Content Types > Text > Java Properties File > Default encoding" field and be sure to click on "Update" before applying the changes:
Add the previously donwloaded JRE as a new Runtime in "Window > Preferences > Java > Installed JREs" and set it as the default. Also set the "Window > Preferences > Java > Compiler > JDK Compliance" accordingly (that would be "1.8" for Java 8).
In order to better spot errors and stack traces, you should considerably increase the console output buffer or remove the limit altogether using the "Window > Preferences > Run/Debug > Console" options:
Node.js (optional)
Node.js is not strictly needed but it can be useful. Install the latest LTS version.
Connect to GIT
Add the repositories
The Yada Framework sources are hosted on the public GitHub site.
To access the GitHub repository use your GitHub credentials on the yadaframework repo.
The git url should be git@github.com:xtianus/yadaframework.git
.
|
Since 2021 the preferred method for connecting to GitHub is ssh. If you didn’t do so already, you should create a pair of SSH private/public keys and upload the public one to GitHub here. The keys can be generated on Windows using PuttyGen. The public one to upload to GitHub is the text shown in the "authorized_keys" box of PuttyGen. The private one to keep on your computer must be exported via the "Conversions > Export OpenSSH key" menu without a passphrase. |
Once you have the repository credentials, connect Eclipse:
-
Open the SSH2 preferences dialog from "Window > Preferences"
-
browse to the location of your private GitHub OpenSSH key
-
Open the "git perspective" from "Window > Perspective > Open Perspective"
-
On the left of the workspace you should see the "Git Repositories" panel
-
Click on "Clone a Git repository" and add the needed information
On the "Branch Selection" dialog you should NOT select "master" but the latest versioned branch, for example "0.7.3".
On the "Local Destination" dialog you need to select a local folder where to clone the repository.
Here we assume that you are going to create a new Eclipse project called MySiteProject
(will be explained later) and
add it to a git repository called mysite
.
It is very important that you store both the Yada Framework repository and your application repository in a common parent folder that is specific to the current project, for example
C:\work\git-mysite
, otherwise relative paths in the build file won’t work and you’ll have to customise them.
For the bookstore example we create a common git folder called C:\work\git-yadabookstore
that will contain the "yadaframework" git repo.
You should create your base git folder before proceeding. The Yada Framework will have to be cloned inside this folder, with a name of "yadaframework":
At the end, when you later also add the "mysite" git and web project, the filesystem structure will be like the following, where "mysite" is any new project that uses the Yada Framework:
C:\work\git-mysite
mysite
.git
MySiteProject
yadaframework
.git
docs
YadaDocs
YadaTools
YadaWeb
... and other folders
When you have connected the yada repository, you can switch to the "Java Perspective" in Eclipse.
The First Project
Creating the Project
Create a new Eclipse Project using "File > New > Project… > Gradle > Gradle Project" menu. Use any name you like ("MySiteProject" in the above example) and accept all defaults. Two new Java projects will be created in your workspace.
For the bookstore example the project name will be YadaBookstore
|
Since Gradle 7 the wizard creates a |
The following fixProject.sh
bash script can be used in order to get rid of the lib
subproject:
#!/bin/bash
# Fix Buildship generated Eclipse project
projectFolder=$1
sed -i "s/include('lib')//g" $projectFolder/settings.gradle
mv $projectFolder/lib/bin $projectFolder
mv $projectFolder/lib/src $projectFolder
mv $projectFolder/lib/.classpath $projectFolder
mv $projectFolder/lib/build.gradle $projectFolder
rm -rf $projectFolder/lib
rm -rf $projectFolder/src/main/java/*
rm -rf $projectFolder/src/test/java/*
You can find a version of it in YadaTools/scripts/fixProject.sh
.
It should be run in the parent folder of the project passing the project folder itself as an argument:
fixProject.sh MySiteProject
|
We don’t currently provide a Windows equivalent so you should run the above script under a windows version of bash.
Git for windows provides such a program in a path like |
After the project has been fixed, select the old projects in Eclipse and do a "refresh" accepting to delete the lib
project when prompted.
Optional: edit the /MySiteProject/gradle/wrapper/gradle-wrapper.properties
file changing the distributionUrl
to match the version of
Gradle that you want to use. For example distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
.
This is only needed if the gradle you want to use is not yet available in Eclipse. Right-click the project and choose "Gradle > Refresh Gradle Project" to update the version of Gradle used.
You can choose to add an external Tomcat server or use the embedded version. In the first case, you should be using the "Enterprise" version of Eclipse. If not, you should at least install the "Eclipse Web Tools Platform" (WTP) plugin.
The bookstore application will use Tomcat embedded, so no need to install any standalone server.
Importing the YadaFramework Projects
After connecting to the GitHub repository as explained above, you can import the needed projects using the "File > Import… > Gradle > Existing Gradle Project" menu.
|
The Yada projects will be located in the git folder you chose before, not along with the new project just created. This may be confusing because the Import dialog will initially open in the workspace and you will have to browse to the git folder instead. After adding the new project to git as explained later, all projects will be found in the git folder. |
To import most Yada Framework projects at once you could just import the "YadaWebCommerce" project and rely on dependency resolution to automatically import everything else. Don’t worry about Gradle versions here: the version used will be the one configured inside the imported projects.
The YadaTools
project should also be imported because you will later need to copy some files from it.
If the imported projects have any errors, first of all right-click and choose "Gradle > Refresh Gradle Project". If it complains about some unbound system library, open "Properties > Java build path > Libraries" and remove the library.
You should now have, in your workspace, the following Yada projects:
-
YadaTools
-
YadaWeb
-
YadaWebCMS
-
YadaWebCommerce
-
YadaWebSecurity
Saving to git
The next step is to create a git repository to store your projects. You must definitely use git to store your files, because after sharing the project to the repo it will be moved to the same root folder of the Yada projects so that relative paths in the build file will work effortlessly.
For a quick test, you can just create a
local repository. It must reside in the same root folder that was used when cloning Yada, that was C:\work\git-mysite
in the previous example. The name can be anything you like. Here we use 'mysite':
In the bookstore tutorial the git folder would be C:\work\git-yadabookstore\yadabookstore
You can also use any public provider like GitHub or a private server installation based for example on GitLab and clone the repository locally, being careful to create the correct directory tree.
When using GitLab, let it create a default readme.md file so that you’ll be able to check out the repository easily (there should be a similar option on GitHub). Then add the repository location to the Git Perspective as done for the Yada Framework.
After the repository has been added to Eclipse, to add your project to the git repository right-click on it and choose "Team > Share Project…". In the dialog you should just select the correct repository and accept the defaults. The resulting filesystem structure will be as follows:
C:\work\git-mysite
mysite
.git
MySiteProject
yadaframework
.git
docs
YadaDocs
YadaTools
YadaWeb
... and other stuff
Finally edit .gitignore
in the root of your project to make it look like the following:
/.gradle/
/build/
/bin/
/.gitattributes
/.settings/
/.classpath
/.project
!gradle-wrapper.jar
/.sass/
**/res/css/*.min.*
**/res/js/*.min.*
If you don’t see the file, you should change the workspace filter. Next to the "Package Explorer" title there’s a menu icon containing the "Filters…" item. Ensure that ".* resources" is NOT selected.
The Build File
Replace your build.gradle
with the contents of /YadaTools/scripts/template.gradle
.
The lines marked with // CHANGE THIS !!!
should be edited to suit your needs.
In the bookstore tutorial we use the ybs
acronym and keep all the defaults.
ext.acronym = 'ybs'
|
The Yada Framework has the concept of "environment" built in: an environment is an installation of the application. There can be a development environemnt that runs on your computer, a test environment that is deployed on a remote server and is used for testing, a production environment that is deployed on a remote serer and is used by the end users. |
The default environments are "dev" for "Development", "tst" for "Test" and "prod" for "Production".
You can rename them (or also add/remove some) using the "envs" property in the yadaInit
task of the build,
but the envs array must always have the "development" environtment first and the "production" environment last
in order to create a correct configuration.xml file. For a list of all other options for the yadaInit
task
see /YadaTools/src/main/groovy/net/yadaframework/tools/YadaProject.groovy
Replace your settings.gradle
with the following:
rootProject.name = 'MySiteProject'
include 'YadaWeb'
project(':YadaWeb').projectDir = "../../yadaframework/YadaWeb" as File
include 'YadaWebSecurity'
project(':YadaWebSecurity').projectDir = "../../yadaframework/YadaWebSecurity" as File
include 'YadaWebCMS'
project(':YadaWebCMS').projectDir = "../../yadaframework/YadaWebCMS" as File
include 'YadaWebCommerce'
project(':YadaWebCommerce').projectDir = "../../yadaframework/YadaWebCommerce" as File
You should change the project name to whatever you used. The above assumes that you cloned the yadaframework repository in the same root folder of your project repository as explained in "Add the repositories" above. This setup is needed to use YadaWeb class files directly instead of going through the jar, and is handy when you plan to work on the YadaWeb sources to fix and improve them.
Use rootProject.name = 'YadaBookstore'
for the bookstore tutorial.
Copy the /YadaTools/scripts/sass.properties
file into the project folder.
Code Generation (just a bit)
Ensure you have these folders in your project before the next step, and create them when missing:
-
src/main/java
-
src/main/webapp
Use the Gradle > Refresh Gradle Project
project menu item to initialise the project.
Check the Elcipse console (Window > Show view > Console) to see if there are any problems.
|
If you get the error "Java compiler level does not match the version of the installed Java project facet"
you may have an inconsistent workspace, probably because you changed the default Gradle options during project creation.
Check that the file |
Open a command prompt in the root folder of your project (e.g. C:\work\git-mysite\MySiteProject
) and run gradlew yadaInit --no-daemon
.
For the bookstore tutorial:
cd C:\work\git-yadabookstore\yadabookstore\YadaBookstore
gradlew yadaInit --no-daemon
This task will add the java core Spring configuration and some default files that later will have to be either deleted or customised. The "--no-daemon" option is to stay on the safe side.
|
You can run the task multiple times and it will never overwrite existing files: to revert a change, delete the file and run the task again |
Refresh your project in Eclipse (F5). If you see compilation errors ensure that you’re just missing some classpath libraries and do a "Refresh Gradle Project" again. If you still have errors, try to fix them ;-) For example you might need to remove the dependency on YadaWebSeurity classes if you didn’t want to use it.
This may be a good time to commit and push to git.
Initial Customization
By default, all files used and created by the application are to be found inside the /srv/<acronym><env>
root
folder,
where <acronym>
is the value of ext.acronym
in the build.gradle
file and <env>
is the "environment",
as explained in The Build File above. This folder should be manually created.
The predefined application user is (literally<EMAIL_ADDRESS>with password myAppPassword
.
Before starting the server for the first time, you may want to change that values by editing
the /src/main/resources/conf.webapp.dev.xml
file at the <user> tag. The data is stored in the database on first startup
and never read from file again, so this is the best time to change it to your likes.
More users can still be added later: only existing users are ignored.
There are some other items that can be customized but they can wait for later:
-
configuration files
-
log configuration
-
startup actions in
Setup.java
For the YadaBookstore application, the default root folder is /srv/ybsdev
. You should change the
admin user email to your own email address in order to receive emails from the application.
Database Setup
The default database schema, user and password are found in /src/main/resources/conf.webapp.dev.xml
, /src/main/resources/META-INF/persistence.xml
, /src/main/webapp/META-INF/context.xml
and
/env/dev/createDatabaseAndUser.bat
. If you want to change them, do so in all files.
For the YadaBookstore application, the default values are:
schema |
ybsdbdev |
user |
ybsuserdev |
password |
mydevpwd |
To create the database and user, run the /env/dev/createDatabaseAndUser.bat
(if you’re not on windows, just copy the content and adapt it to your platform).
On Windows you can open a cmd window and drag the file there from Eclipse, then just press Enter.
If all goes well, you can now generate an initial schema by running the dbSchema
gradle task. The schema
will be derived from the classes listed in /src/main/resources/META-INF/persistence.xml
so, to simplify the schema,
you may revise this file and remove any unneeded classes. You will always be able to remove/add a class later
though you will have to manually delete the removed classes from the database too.
As you will be generating a new schema often, you should create a "Run Configuration" in Eclipse to generate the schema. Click on "Run > Run Configurations… > Gradle Task" then press the "New launch configuration" icon. A new configuration called "New_configuration" will be created. Change the name to anything you like, for example "MySiteProject - DB Schema", add a Gradle Task named "dbSchema", set your application project as the Working Directory, then in the "Common" tab choose "Save as > Shared file" and type "\MySiteProject\Launches"
You can now run the schema creation task with a press of a button.
If all goes well, the output will be written to the /schema
folder (you might need to refresh it in Eclipse with F5).
To load the schema into the database, use the /env/dev/dropAndCreateDatabase.bat
script (or an equivalent linux/mac version).
You may get some compilation errors that need to be fixed before trying the generation again.
If the schema generator can’t connect to the database check that all configuration files listed above (persistence.xml
etc.) have the correct DB login credentials.
Tomcat server
This section is about setting up a standalone Tomcat server that can be controlled from Eclipse.
|
You can skip this section if you’re going to use the embedded version of Tomcat (recommended option for a generic use case). |
Skip this section for the bookstore tutorial.
Download Apache Tomcat 8.5 "64-bit Windows zip" and unzip the folder to some place like C:\local\Tomcats\apache-tomcat-8.5.51
.
Create a new folder where you will keep all your web application deploys, like C:\local\Deploy
.
In Eclipse, while in the "Java Perspective", show the "Servers" view from "Window > Show View > Other… > Server > Servers".
You will see the link "No servers available. Click to create a new server…". Click that link. You will see a dialog
where you should choose "Apache > Tomcat v8.5 Server". In the Next dialog choose your "Tomcat installation directory",
for example C:\local\Tomcats\apache-tomcat-8.5.51
, and finish.
Just to be safe, check that Tomcat works by running it and browsing to http://localhost:8080/
. If all is fine, you should see
an error from Tomcat:
Stop Tomcat then right-click on it and choose "Open". You will see the Overview:
On this page do the following:
-
Under "Server locations" set "Use custom location > Server path" to
C:\local\Deploy\myProject
where "myProject" is anything you like -
Under "Server Options" uncheck "Modules auto reload by default"
-
Under "Timeouts" add a trailing 0 to both timeouts so that 45 becomes 450 and 15 becomes 150
-
Save with CTRL+S.
If your sources in the "Package Explorer" window don’t have any red marks (no compilation errors), you can add the web application to Tomcat:
-
Right-click on the Tomcat server in the "Servers" view
-
Select "Add and Remove… > Add All >>"
If the server starts with no errors, you can see the homepage placeholder at http://localhost:8080/
Embedded Tomcat
This section is about running the embedded Tomcat server.
|
You can skip this section if you’re going to use the standalone version of Tomcat. |
The application is started by running the net.yadaframework.core.YadaTomcatServer
class.
It accepts two mandatory arguments and an optional one:
- acronym+environment
-
a string that will be used by the shutdown command
- webapp folder
-
relative path of the webapp folder in eclipse ("src/main/webapp"), or the full path elsewhere
- temp folder
-
optional in Eclipse, it must be the full path of the temp folder for Tomcat data (where the war is exploded)
When the last argument is missing, "developer mode" is assumed and the HTTPS connector is enabled. It’s not
enabled in all other environments because HTTPS is supposed to be handled by Apache.
The YadaTomcatServer
class can be overridden or rewritten to achieve a different behavior.
The command line to start the application on the developer PC is something like
java net.yadaframework.core.YadaTomcatServer ybsdev src/main/webapp
This can be easily added in Eclipse as a "Java Application" entry in the "Run Configurations" dialog.
After running the server, point the browser to http://localhost:8080/ in order to see a skeleton home page.
Troubleshooting
Compilation Errors
In case of compilation errors, the first thing to do is to run a "Refresh Gradle Project" on the affected project or the including project. If errors persist, check that you have imported all the needed Yada projects. Also be sure to have "Projects > Build Automatically" checked and try with a "Project > Clean…".
Validation Errors
If you get an error like
CHKJ3000E: WAR Validation Failed: org.eclipse.jst.j2ee.commonarchivecore.internal.exception.DeploymentDescriptorLoadException: WEB-INF/web.xml
you may fix it just by forcing a validation on the project via the menu.
Tomcat Startup Errors
If Tomcat doesn’t start, it might have stale data. Try with a "Clean…" on the server. If everything fails, stop the server and delete the content of the Deploy folder,
for example C:\local\Deploy\myProject
. Then do a "Publish" on the server. If you can’t delete some file because Windows says it’s open, you’ll need to quit Eclipse
and be sure that there are no ghost Tomcat processes running. In extreme cases, you might need to restart your PC.


This form is for forestry workers in northern-interior British Columbia. We will match workers’ needs and goals to services and jobs.
Time: The average time to complete the application form is about 5 - 10 minutes. At this time, you will need to complete your application in one session. This service will not save your information.
Instructions: Complete the form and press submit to send your application to our Forestry Job Placement Support Team. We will contact you as soon as possible.
Privacy Protection:
The personal information on this form is collected by the Ministry of Labour for the purpose of Forestry
Worker Job Placement Program under the authority of section 26 (C) and 27 (2) of The Freedom of Information
and Protection of Privacy Act. Questions about the collection or use of this information can contact the Forestry Worker Job Placement Program, 1-844-478-0822.


Registration Form
First Name
Last Name
Father's Name
Mother's Name
Address
City
State
Zip
Phone Number
Email Address
Submit


Police: Home in California Backyard Shooting Was Targeted
by ASSOCIATED PRESSFRESNO, Calif. (AP) — Police searched Monday for two assailants who sneaked into a backyard party in Fresno and opened fire while a small crowd watched football on television. Four people were killed and six wounded.
Fresno Police Chief Andrew Hall said the house where a group of Hmong family and friends had gathered was deliberately targeted on Sunday evening, but the shooters fired at random once they saw men in the backyard watching football.
“They fired randomly into the crowd. It does not appear that they were targeting any individuals, and once they fired, they fled,” he said.
Hall said there was no immediate word on a motive, and so far there was no indication that any of the victims knew their attackers, though most could not see them, describing seeing only a flashing light when the semi-automatic pistols were fired. Officers went door-to-door looking for surveillance video that might help them track down suspects.
The party “was not described as being out of control in any way. It was just a family event,” Hall said.
The men who died were ages 21 to 40, he said.
Three were found dead when emergency crews arrived. A fourth person died at a hospital. Six others were hospitalized in critical condition but expected to survive, authorities said.
Hall said police were investigating whether the shooting was connected to a recent disturbance involving some of the people at the party. He also announced the establishment of an Asian gang task force ahead of the Hmong New Year, out of concern about the possibility of more violence.
California has the nation’s highest population of Hmong, a Southeast Asian ethnic minority.
Residents complained that other shootings have happened recently in the same neighborhood. The home where the attack took place is near an industrial area with commercial buildings near the Fresno airport.
Calvin Gatison, who lives on the same block, said the street had been peaceful for years, but at least two shootings happened in recent weeks.
Gatison said his street of single-family homes with manicured front yards is quiet during the week. On weekends, grandfathers can be seen playing with grandchildren in their front yards, while other neighbors host gatherings in their yards, but typically without loud music.
“For something like this to happen in a quiet neighborhood, it scares you,” he said. “This is the third shooting I’ve heard since the middle of September.”
Choua Vang told the Bee that his neighbor’s house was shot at last week and that he feels unsafe outside after dark.
“We’re thinking about moving out of the neighborhood,” he said. “We don’t know how many more shootings there will be.”
It was at least the second fatal gun attack Sunday in Fresno, the Fresno Bee reported. A man in his 20s was shot to death early Sunday at a home in another part of the city. Police have not said whether the incidents could be connected.
The killings came after two other mass shootings in California in what has been an especially violent year for the state.
Last Thursday, a 16-year-old student at a Southern California high school in the city of Santa Clarita outside Los Angeles shot and killed two classmates and wounded three other teens before shooting himself in the head. He died the next day.
 A fourth son wounded in the shooting was on life support on Sunday, family members said.
Rep. Jim Costa, a Democrat who represents the Fresno area in Congress, said he was saddened to hear of another shooting “this time in my own district.” He said in a statement that “thoughts and prayers are not enough!” and urged the Senate to take up gun-control legislation.
There have been eight mass killings in California so far this year, claiming the lives of 33 people, according to a database compiled by The Associated Press, USA Today and Northeastern University.
That’s a dramatic increase from previous years, with eight mass killings in the state for all of 2016-2018.
The increase mirrors the national trend in 2019. There have been 39 mass killings this year in the United States, compared with 25 in 2018, according to the database, which tracks homicides where four or more people are killed, not including the offender.
By OLGA R. RODRIGUEZ Associated Press
Copyright © 2019 courthousenews.com


MCQ Mock Exam
Take a randomly generated test to sharpen your exam skills.
These additional quizzes and crib sheets are provided to aid with revision of important topics.


Psalms 98:4
Make a joyful noise unto the LORD, all the earth: make a loud noise, and rejoice, and sing praise.


Progress Bar 0 % 0 /36 Progress 3D Progress Striped (can animate stripe example 2) Progress Bar with Text 0 %


Zend Framework
LICENSE
This source file is subject to the new BSD license that is bundled with this package in the file LICENSE.txt. It is also available through the world-wide-web at this URL: http://framework.zend.com/license/new-bsd If you did not receive a copy of the license and are unable to obtain it through the world-wide-web, please send an email to<EMAIL_ADDRESS>so we can send you a copy immediately.
__construct(
string $host, int|null $port
=
null, bool $ssl
=
false
)
:
void
Public constructor
| Name | Type | Description |
|---|---|---|
| $host | string | hostname or IP address of IMAP server, if given connect() is called |
| $port | int|null | port of IMAP server, null for default (143 or 993 for ssl) |
| $ssl | bool | use ssl? 'SSL', 'TLS' or false |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
_assumedNextLine(
string $start
)
:
bool
get next line and assume it starts with $start. some requests give a simple feedback so we can quickly check if we can go on.
| Name | Type | Description |
|---|---|---|
| $start | string | the first bytes we assume to be in the next line |
| Type | Description |
|---|---|
| bool | line starts with $start |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
_decodeLine(
string $line
)
:
array
split a given line in tokens. a token is literal of any form or a list
| Name | Type | Description |
|---|---|---|
| $line | string | line to decode |
| Type | Description |
|---|---|
| array | tokens, literals are returned as string, lists as array |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
_nextLine(
)
:
string
get the next line from socket with error checking, but nothing else
| Type | Description |
|---|---|
| string | next line |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
_nextTaggedLine(
string $tag
)
:
string
get next line and split the tag. that's the normal case for a response line
| Name | Type | Description |
|---|---|---|
| $tag | string | tag of line is returned by reference |
| Type | Description |
|---|---|
| string | next line |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
append(
string $folder, string $message, array $flags
=
null, string $date
=
null
)
:
bool
append a new message to given folder
| Name | Type | Description |
|---|---|---|
| $folder | string | name of target folder |
| $message | string | full message content |
| $flags | array | flags for new message |
| $date | string | date for new message |
| Type | Description |
|---|---|
| bool | success |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
capability(
)
:
array
Get capabilities from IMAP server
| Type | Description |
|---|---|
| array | list of capabilities |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
connect(
string $host, int|null $port
=
null, string|bool $ssl
=
false
)
:
string
Open connection to IMAP server
| Name | Type | Description |
|---|---|---|
| $host | string | hostname or IP address of IMAP server |
| $port | int|null | of IMAP server, default is 143 (993 for ssl) |
| $ssl | string|bool | use 'SSL', 'TLS' or false |
| Type | Description |
|---|---|
| string | welcome message |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
copy(
string $folder, $from, int|null $to
=
null
)
:
bool
copy message set from current folder to other folder
| Name | Type | Description |
|---|---|---|
| $folder | string | destination folder |
| $from | ||
| $to | int|null | if null only one message ($from) is fetched, else it's the last message, INF means last message avaible |
| Type | Description |
|---|---|
| bool | success |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
create(
string $folder
)
:
bool
create a new folder (and parent folders if needed)
| Name | Type | Description |
|---|---|---|
| $folder | string | folder name |
| Type | Description |
|---|---|
| bool | success |
delete(
string $folder
)
:
bool
remove a folder
| Name | Type | Description |
|---|---|---|
| $folder | string | folder name |
| Type | Description |
|---|---|
| bool | success |
escapeList(
array $list
)
:
string
escape a list with literals or lists
| Name | Type | Description |
|---|---|---|
| $list | array | list with literals or lists as PHP array |
| Type | Description |
|---|---|
| string | escaped list for imap |
escapeString(
string|array $string
)
:
string|array
escape one or more literals i.e. for sendRequest
| Name | Type | Description |
|---|---|---|
| $string | string|array | the literal/-s |
| Type | Description |
|---|---|
| stringarray | escape literals, literals with newline ar returned as array('{size}', 'string'); |
examine(
string $box
=
INBOX
)
:
bool|array
examine folder
| Name | Type | Description |
|---|---|---|
| $box | string | examine this folder |
| Type | Description |
|---|---|
| boolarray | see examineOrselect() |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
examineOrSelect(
string $command
=
EXAMINE, string $box
=
INBOX
)
:
bool|array
Examine and select have the same response. The common code for both is in this method
| Name | Type | Description |
|---|---|---|
| $command | string | can be 'EXAMINE' or 'SELECT' and this is used as command |
| $box | string | which folder to change to or examine |
| Type | Description |
|---|---|
| boolarray | false if error, array with returned information otherwise (flags, exists, recent, uidvalidity) |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
fetch(
string|array $items, int $from, int|null $to
=
null
)
:
string|array
fetch one or more items of one or more messages
| Name | Type | Description |
|---|---|---|
| $items | string|array | items to fetch from message(s) as string (if only one item) or array of strings |
| $from | int | message for items or start message if $to !== null |
| $to | int|null | if null only one message ($from) is fetched, else it's the last message, INF means last message avaible |
| Type | Description |
|---|---|
| stringarray | if only one item of one message is fetched it's returned as string if items of one message are fetched it's returned as (name => value) if one items of messages are fetched it's returned as (msgno => value) if items of messages are fetchted it's returned as (msgno => (name => value)) |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
listMailbox(
string $reference, string $mailbox
=
*
)
:
array
get mailbox list
this method can't be named after the IMAP command 'LIST', as list is a reserved keyword
| Name | Type | Description |
|---|---|---|
| $reference | string | mailbox reference for list |
| $mailbox | string | mailbox name match with wildcards |
| Type | Description |
|---|---|
| array | mailboxes that matched $mailbox as array(globalName => array('delim' => .., 'flags' => ..)) |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
login(
string $user, string $password
)
:
bool
Login to IMAP server.
| Name | Type | Description |
|---|---|---|
| $user | string | username |
| $password | string | password |
| Type | Description |
|---|---|
| bool | success |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
readLine(
array|string $tokens
=
array, string $wantedTag
=
*, bool $dontParse
=
false
)
:
bool
read a response "line" (could also be more than one real line if response has {..}
| Name | Type | Description |
|---|---|---|
| $tokens | array|string | decoded tokens are returned by reference, if $dontParse is true the unparsed line is returned here |
| $wantedTag | string | check for this tag for response code. Default '*' is continuation tag. |
| $dontParse | bool | if true only the unparsed line is returned $tokens |
| Type | Description |
|---|---|
| bool | if returned tag matches wanted tag |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
readResponse(
string $tag, bool $dontParse
=
false
)
:
null|bool|array
read all lines of response until given tag is found (last line of response)
| Name | Type | Description |
|---|---|---|
| $tag | string | the tag of your request |
| $dontParse | bool | if true every line is returned unparsed instead of the decoded tokens |
| Type | Description |
|---|---|
| nullboolarray | tokens if success, false if error, null if bad request |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
rename(
string $old, string $new
)
:
bool
rename an existing folder
| Name | Type | Description |
|---|---|---|
| $old | string | old name |
| $new | string | new name |
| Type | Description |
|---|---|
| bool | success |
requestAndResponse(
string $command, array $tokens
=
array, bool $dontParse
=
false
)
:
mixed
send a request and get response at once
| Name | Type | Description |
|---|---|---|
| $command | string | command as in sendRequest() |
| $tokens | array | parameters as in sendRequest() |
| $dontParse | bool | if true unparsed lines are returned instead of tokens |
| Type | Description |
|---|---|
| mixed | response as in readResponse() |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
select(
string $box
=
INBOX
)
:
bool|array
change folder
| Name | Type | Description |
|---|---|---|
| $box | string | change to this folder |
| Type | Description |
|---|---|
| boolarray | see examineOrselect() |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
sendRequest(
string $command, array $tokens
=
array, string $tag
=
null
)
:
null
send a request
| Name | Type | Description |
|---|---|---|
| $command | string | your request command |
| $tokens | array | additional parameters to command, use escapeString() to prepare |
| $tag | string | provide a tag otherwise an autogenerated is returned |
| Type | Description |
|---|---|
| null |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |
store(
array $flags, int $from, int|null $to
=
null, string|null $mode
=
null, bool $silent
=
true
)
:
bool|array
set flags
| Name | Type | Description |
|---|---|---|
| $flags | array | flags to set, add or remove - see $mode |
| $from | int | message for items or start message if $to !== null |
| $to | int|null | if null only one message ($from) is fetched, else it's the last message, INF means last message avaible |
| $mode | string|null | '+' to add flags, '-' to remove flags, everything else sets the flags as given |
| $silent | bool | if false the return values are the new flags for the wanted messages |
| Type | Description |
|---|---|
| boolarray | new flags if $silent is false, else true or false depending on success |
| Exception | Description |
|---|---|
| \Zend_Mail_Protocol_Exception |


Persistent Overlays¶
Persistent overlay images were added in version 2.4. This feature allows you to overlay a writable file system on an immutable read-only container for the illusion of read-write access.
Overview¶
A persistent overlay is an image that “sits on top” of your compressed, immutable squashfs container. When you install new software or create and modify files the overlay image stores the changes.
In Singularity versions 2.4 and later an overlay file system is automatically added to your squashfs or sandbox container when it is mounted. This means you can install new software and create and modify files even though your container is read-only. But your changes will disappear as soon as you exit the container.
If you want your changes to persist in your container across uses, you
can create a writable image to use as a persistent overlay. Then you
can specify that you want to use the image as an overlay at runtime
with the --overlay
option.
You can use a persistent overlays with the following commands:
run
exec
shell
instance.start
Usage¶
To use a persistent overlay, you must first have a container.
$ singularity build ubuntu.simg shub://GodloveD/ubuntu
Then you must create a writable, ext3 image. We can do so with the image.create
command:
$ singularity image.create my-overlay.img
Now you can use this overlay image with your container. Note that it is not necessary to be root to use an overlay partition, but this will ensure that we have write privileges where we want them.
$ sudo singularity shell --overlay my-overlay.img ubuntu.simg
Singularity ubuntu.simg:~> touch /foo
Singularity ubuntu.simg:~> apt-get install -y vim
Singularity ubuntu.simg:~> which vim
/usr/bin/vim
Singularity ubuntu.simg:~> exit
You will find that your changes persist across sessions as though you were using a writable container.
$ sudo singularity shell --overlay my-overlay.img ubuntu.simg
Singularity ubuntu.simg:~> ls /foo
/foo
Singularity ubuntu.simg:~> which vim
/usr/bin/vim
Singularity ubuntu.simg:~> exit
If you mount your container without the --overlay
option, your changes will be
gone.
$ sudo singularity shell ubuntu.simg
Singularity ubuntu.simg:~> ls /foo
ls: cannot access 'foo': No such file or directory
Singularity ubuntu.simg:~> which vim
Singularity ubuntu.simg:~> exit


Health First
Home
Gallery
Events
Best Body Builders
What We Do
Aerobics
Body Building
Yoga
Contact Us
Holidays
Daily Schedule


Am I Normal Yet?, How Hard Can Love Be?, What’s a Girl Gotta Do? and …And a Happy New Year?, by Holly Bourne
This series, featuring Evie, Amber and Lottie, introduced me to feminism. The girls face down tough issues with the combined powers of friendship, feminism and cheesy snacks. Funny, powerful and definitely relatable.
Moxie, by Jennifer Mathieu
“Vivian Carter is fed up. Fed up with her high school teachers who think the football team can do no wrong. Fed up with sexist dress codes, hallway harassment and gross comments from guys during class. But most of all, Viv Carter is fed up with always following the rules.
“Viv’s mum was a tough-as-nails, punk rock Riot Grrrl in the ’90s, and now Viv takes a page from her mother’s past and creates Moxie, a feminist zine that she distributes anonymously to her classmates. She’s just blowing off steam, but other girls respond and spread the Moxie message.
“As Viv forges friendships with other young women across the divides of cliques and popularity rankings, she realises that what she has started is nothing short of a girl revolution.”
This book will leave you feeling empowered and wanting to be a Moxie girl yourself. You can read my review of Moxie here.
The Exact Opposite of Okay, by Laura Steven
“Izzy O’Neill here! Impoverished orphan, aspiring comedian and Slut Extraordinaire, if the gossip sites are anything to go by.
“Izzy never expected to be 18 and internationally reviled. But when explicit photos involving her, a politician’s son and a garden bench are published online, the trolls set out to take her apart. Armed with best friend Ajita and a metric ton of nachos, she tries to laugh it off - but as the daily slut-shaming intensifies, she soon learns the way the world treats teenage girls is not okay.”
This timely ‘feminist firecracker of a novel’ comes out today and I can’t wait to read it!
The Disreputable History of Frankie Landau-Banks, by E. Lockhart
“As a girl, Frankie knows she is supposed to play by the rules, but the rules were made by old boys for boys and therefore are not her rules to begin with. She decides to take matters into her own hands, outsmarting the secret all-male society at her rich kid boarding school by tricking the members, including her own boyfriend, into believing her increasingly outrageous commands are their leader’s.
“Undaunted by her male peers, Frankie contests the old-fashioned idea that it’s the boys who rule the school proving that girls are not to be dismissed - even by her own father, a former member of the all-male society himself!”
The author of Genuine Fraud has done it again. E. Lockhart is making a habit of including strong women in her novels and I am so on board with it.
Things a Bright Girl Can Do, by Sally Nicholls
“Evelyn is 17, and though she is rich and clever, she may never be allowed to follow her older brother to university. Enraged that she is expected to marry her childhood sweetheart rather than be educated, she joins the Suffragettes, and vows to pay the ultimate price for women’s freedom.
“May is 15, and already sworn to the cause, though she and her fellow Suffragists refuse violence. When she meets Nell, a girl who’s grown up in hardship, she sees a kindred spirit. Together and in love, the two girls start to dream of a world where all kinds of women have their place.
“But the fight for freedom will challenge Evelyn, May and Nell more than they ever could believe. As war looms, just how much are they willing to sacrifice?”
This is fresh take on the Suffragette movement and a timely read for the centenary of women getting the vote. But don’t forget about the struggle many other women had to continue fighting to get their vote.
Read Amélie’s review of Things a Bright Girl Can Do →
Girls Made of Snow and Glass, by Melissa Bashardoust
“At 16, Mina’s mother is dead, her magician father is vicious, and her silent heart has never beat with love for anyone. She never guessed that her father cut out her heart and replaced it with one of glass. When she moves to Whitespring Castle and sees its king, Mina plans to win the king’s heart with her beauty, become queen, and finally know love.
“15-year-old Lynet looks just like her late mother, and one day she discovers why: a magician created her out of snow in the dead queen’s image. But despite being the dead queen made flesh, Lynet would rather be like her fierce and regal stepmother, Mina. She gets her wish when her father makes Lynet queen of the southern territories, displacing Mina. Now Mina is starting to look at Lynet with something like hatred, and Lynet must decide what to do - and who to be - to win back the only mother she’s ever known, or else defeat her once and for all.”
A feminist, dark re-telling of Snow White and a must-read for those who loved Because You Love To Hate Me.
Warcross, by Marie Lu
“In this sci-fi thriller, Marie Lu conjures an immersive, exhilarating world where choosing who to trust may be the biggest gamble of all.”
Emika Chen is a character that will stay with you long after the final page, with her strength and intelligence.
The Hate U Give, by Angie Thomas
“Sixteen-year-old Starr lives in two worlds: the poor neighbourhood where she was born and raised and her posh high school in the suburbs. The uneasy balance between them is shattered when Starr is the only witness to the fatal shooting of her unarmed best friend, Khalil, by a police officer. Now what Starr says could destroy her community. It could also get her killed.”
A book that’s destined to become a modern classic. It’s all about the power of your voice and it deals with intersectional feminism, as it’s both Starr’s race and gender that allows others to dismiss her, at least at first.
Wonder Woman: Warbringer, by Leigh Bardugo
“She will become a legend but first she is Diana, Princess of the Amazons. And her fight is just beginning.
“Diana is desperate to prove herself to her warrior sisters. But when the opportunity comes, she throws away her chance at glory and breaks Amazon law to save a mere mortal, Alia Keralis. With this single heroic act, Diana may have just doomed the world. Alia is a Warbringer - a descendant of the infamous Helen of Troy, fated to bring about an age of bloodshed and misery.
“Diana and Alia will face an army of enemies, mortal and divine, determined to destroy or possess the Warbringer. To save the world, they must stand side by side against the tide of war.”
This novel is all about the power of female friendships and is jam-packed with girl power. If you liked the film last year, you’ll love this awesome YA interpretation. Read my review of Wonder Woman: Warbringer.
Girl Up, by Laura Bates
“They told you you need to be thin and beautiful. They told you to wear longer skirts, avoid going out late at night and move in groups - never accept drinks from a stranger, and wear shoes you can run in more easily than heels.
 They warned you that if you try to be strong, or take control, you’ll be shrill, bossy, a ballbreaker. Of course it’s fine for the boys, but you should know your place.
“They told you ‘that’s not for girls’ - ‘take it as a compliment’ - ‘don’t rock the boat’ - ‘that’ll go straight to your hips’. They told you ‘beauty is on the inside’, but you knew they didn’t really mean it. Well I’m here to tell you something different.”
From the founder of Everyday Sexism, Girl Up is a guide for teenage girls that covers everything from body image to consent. A thought-provoking and funny non-fiction book.
Between Shades of Gray, by Ruta Sepetys
“One night 15-year-old Lina, her mother and young brother are hauled from their home by Soviet guards, thrown into cattle cars and sent away. They are being deported to Siberia.
“An unimaginable and harrowing journey has begun. Lina doesn’t know if she’ll ever see her father or her friends again. But she refuses to give up hope.”
This book sheds light on a part of history that I was unfamiliar with and features Lina, a brave and strong woman who survives awful atrocities, but also has a vulnerable side. I thought this was appropriate for Women’s’ History Month, as it explores a historical era few YA authors have tackled.
It Only Happens in the Movies, by Holly Bourne
“Audrey is over romance. Since her parents’ relationship imploded her mother’s been catatonic, so she takes a cinema job to get out of the house. But there she meets wannabe film-maker Harry. Nobody expects Audrey and Harry to fall in love as hard and fast as they do. But that doesn’t mean things are easy. Because real love isn’t like the movies.”
And we’re back to where it all began. Holly Bourne is a force to reckoned with within UKYA. This book and The Manifesto On How To Be Interesting are witty take-downs of parts of our society and it’s the way that she weaves feminist ideas into her stories that just amazes me.


--- title: Spelling Captcha google_font: Open+Sans:400,700 --- {% include head.html %}
Hello, and thank you for your interest in The Internet.
Please type the correct sequence of characters into the field below.


The Early 80s Music "Top 10"
The Years (1980-1983)
Not in order of importance...
- "Toto" by Africa
- "Magic" by Olivia Newton John
- "Jessie's Girl" by Rick Springfield
- "Down Under" by Men at Work
- "Billie Jean" by Micheal Jackson
- "Take on Me" by A-ha
- "Hungry Like the Wolf" by Duran Duran
- "Video Killed the Radio Star" by The Buggles
- "Total Eclipse of the Heart" by Bonnie Tyler
- "One Thing Leads to Another" by The Fixx
Mid 80s page


-
Banana Breadby MozillaBananaBread is a 3D first-person shooter based on Cube 2: Sauerbraten.

-
Dead Trigger IIby Madfinger GamesEnter a world where humanity is engaged in the ultimate battle for survival in the face of a global Zombie apocalypse.
-
Dungeon Furyby Dungeon FuryBeware of the brutish Orcs in this awesome 3D action game that tests your reflexes to the very limit!
-
HexGLby BKCoreHexGL is a futuristic, fast-paced racing game built by Thibaut Despoulain using HTML5, Javascript and WebGL and a tribute to the original Wipeout and F-Zero series.
-
Monster Madnessby PlayverseFour friends get together for a party while the parents are away, but somebody … or something, is about to crash the party.
-
Pokemonby NintendoFull Screen Pokemon is an open source HTML5 remake of the original Pokemon games. You can play the original two generations (Red/Blue through Crystal), literally billions of procedurally generated maps, or create your own using the level editor.
-
Quake 3 Arenaby TojiCodeWelcome to the Arena, where high-ranking warriors are transformed into spineless mush. Abandoning every ounce of common sense and any trace of doubt, you lunge onto a stage of harrowing landscapes and veiled abysses.
-
Roller Coaster VRby Google ChromeBuckle up for a ride in a roller coaster. Virtual reality at its finest.
-
Urban Galaxyby Urban GalaxyUrban Galaxy is an HTML5 3D real-time MMORPG set in a futuristic Megapolis. Choose your career and play in rich urbanscapes with your friends.


Open Data Files
Here is a list of all the open data files used to create the maps on this website:
- Census Zones - GIS areas for census data (www.censusmapper.ca)
» censuszones.json - Census Data (www.censusmapper.ca)
» demographics.json - Sidewalk Priorities - Streets with Both Sides Cleared (manual plotting of data from City of St. John's map)
» doubleside.json - Sidewalk Priorities - Streets with Single Side Cleared (manual plotting of data from City of St. John's map)
» singleside.json - Sidewalk Priorities - Priority 1 Streets (manual plotting of data from City of St. John's map)
» priority_1.json - Sidewalk Priorities - Priority 2 Streets (manual plotting of data from City of St. John's map)
» priority_2.json - Sidewalk Priorities - Priority 3 Streets (manual plotting of data from City of St. John's map)
» priority_3.json - Sidewalk Priorities - School Zones (manual plotting of data from City of St. John's map)
» school_zones.json - Hospitals & Medical Clinics (walk-in, etc.)
» hospitals.json - Schools, University & College
» schools.json


Preparation time
overnight
Cooking time
over 2 hours
Serves
Serves 4
Invest in the cooking time and you’ll be rewarded with a lovely rich soup that really makes the most of this cheap cut of meat.
Heat the oil in a large, flameproof casserole dish. Add the oxtail pieces and fry over a high heat until browned all over – you may have to do this in batches. Take the oxtail out of the dish and put it on a plate.
Add all the vegetables to the casserole and cook for 4-5 minutes, stirring occasionally. Add the herbs, peppercorns and tomato purée and cook for another minute. Pour in the wine and allow to bubble for a few minutes.
Return the oxtail to the pan and pour in the stock. Bring to the boil, then turn down the heat to a gentle simmer. Cover and cook for 2½-3 hours, until the oxtail is really tender and falling off the bone. Using tongs or a slotted spoon, transfer the oxtail pieces to a plate and leave to cool.
Strain the cooking liquor through a sieve into a bowl and leave to cool. Shred the oxtail meat and discard the bones. Place in a bowl and cover. Cover the bowl of cooking liquor and leave in the fridge, along with the oxtail meat, overnight.
The next day, scrape away and discard the layer of fat on top of the cooking liquid. Spoon the remaining jellified soup into a pan and heat through until simmering. In a small bowl, blend the flour and three tablespoons of the hot soup until smooth. Whisk into the soup and simmer for two minutes. Add the oxtail meat to the pan and heat through for a minute or two before serving.


FAQ
Can I export the sheets as PDF?
Yes, if you install a pdf printer like Free PDF
Can I import data from Cadet?
Since Version 1.1.0 importing Cadet Plane files is possible.
Loadouts and Pilots are not supported yet.
How can I use the FF5 Loadout Houserule?
Just look here.
How can I add new weapons, special characteristics, etc. ?
You will find your anwers on this page.