tag:blogger.com,1999:blog-3083612327956159292024-03-21T04:23:52.799+02:00Just 3adlySharing code snippets from trials and troubles.Unknownnoreply@blogger.comBlogger86125tag:blogger.com,1999:blog-308361232795615929.post-89680349216504671322020-04-06T20:45:00.002+02:002020-04-06T20:57:25.784+02:00Creating a prayer times action on Google Home<div dir="ltr" style="text-align: left;" trbidi="on">
Saving a lot of time during the COVID-19 shutdown gave me the chance to do something I wanted to try for a while, getting my Google Home Mini to tell me the prayer time instead of looking at a screen. So this weekend I got to play with a dummy piece of code I made during the week and finalize my test version of my own prayer times assistant.<br />
<br />
So far, I can only ask about the next prayer or name a specific prayer to know its time.<br />
<br />
Important note: you cannot replicate the following project without a premium account on Firebase to enable calls to external APIs.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWducs5f7QFlinGBCJ6V6xR-mCYrEImYVc_Lse2hSZFfXKdFogB-KbdUymguozgEm5_zPw6dsBKdkRXzls1HaHMFp8CDtmQHFTDa-fUId0li1ju0RmhiPFyBeFAk-R3II9ye6vhGCqoh0/s1600/1+-+test.png" imageanchor="1"></a><img border="0" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWducs5f7QFlinGBCJ6V6xR-mCYrEImYVc_Lse2hSZFfXKdFogB-KbdUymguozgEm5_zPw6dsBKdkRXzls1HaHMFp8CDtmQHFTDa-fUId0li1ju0RmhiPFyBeFAk-R3II9ye6vhGCqoh0/s400/1+-+test.png" width="400" /><br />
<br />
Here is what I had to do in simple words:<br />
<br />
<h2 style="text-align: left;">
1- Create a new Dialogflow agent</h2>
<br />
Dialogflow is where I started my project with creating a blank agent with nothing but a Welcome intent.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIADaB5NXJ_44B0LhSr5CdgKW8BDl7HOezfTssU2TgrjBrDYi_wDxX9xTCvW7BItUWcxwIiMFY-z7OaeHWV75n7s3eL85IOUAFr3bIerFS9eHGG-q2SmP9btgG6bjO_sUI14LjOAr-vj0/s1600/0+-+new.png" imageanchor="1"><img border="0" height="185" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIADaB5NXJ_44B0LhSr5CdgKW8BDl7HOezfTssU2TgrjBrDYi_wDxX9xTCvW7BItUWcxwIiMFY-z7OaeHWV75n7s3eL85IOUAFr3bIerFS9eHGG-q2SmP9btgG6bjO_sUI14LjOAr-vj0/s400/0+-+new.png" width="400" /></a><br />
<br />
<h2 style="text-align: left;">
2 - Modify the default welcome intent</h2>
<br />
For the sake of having fun, I changed the way of addressing the user to say the magic word: "Salam! How can I help you?" plus accepting similar magic words like "Assalm Alaykom".<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwGJAujGt8sm_B4ZZZtWDMJtYxwDEM_YjJ4d6bKm3ZW_im7_oP6lkVC5wNFms4MTv2jgCDETNBa-QdaKngz6PyrrYOuzYSW0-s3P51s1blUpAI4iRE0a3f2I4qNNmjbnxBCv2YArpvBg8/s1600/2+-+welcome.png" imageanchor="1"><img border="0" height="182" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwGJAujGt8sm_B4ZZZtWDMJtYxwDEM_YjJ4d6bKm3ZW_im7_oP6lkVC5wNFms4MTv2jgCDETNBa-QdaKngz6PyrrYOuzYSW0-s3P51s1blUpAI4iRE0a3f2I4qNNmjbnxBCv2YArpvBg8/s400/2+-+welcome.png" width="400" /></a><br />
<br />
<h2 style="text-align: left;">
3 - Create a new intent for the next prayer time</h2>
<br />
This intent is meant to looks for the next prayer time in the array of today's prayers. But first, in which city or location?<br />
<br />
<h3 style="text-align: left;">
Circumvent the location access step</h3>
<br />
To get the user's location, I need to ask for permissions. I didn't want one extra step to ask for permission. I actually don't know if I will have to do this every time or I can add checks once and then I have the location for always. But to cut the working time, I decided to let the user simply mention the city. This can make it faster to develop my agent for now. So I added some training phrases with and without the city parameter. But notice that I added the city parameter as required and added some prompts to ask the user if the parameter is not found in the phrase. I also helped the training by double-clicking the word "Berlin" and chose my city parameter from that popup list which appeared.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8cEL-fPDPjNe3VceMNCX3DiPkpi5WBNHgLjpqnrXeI799mdl3-Bsw35IwOEOvSfmiMKbrwjr0MOOD4otOgpJ-EIoS08wz79VrmNGVYrjtdvwSKuE_N-vagljE8aVRGyrS1L0438d6TCY/s1600/3+-+next+prayer+1.png" imageanchor="1"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8cEL-fPDPjNe3VceMNCX3DiPkpi5WBNHgLjpqnrXeI799mdl3-Bsw35IwOEOvSfmiMKbrwjr0MOOD4otOgpJ-EIoS08wz79VrmNGVYrjtdvwSKuE_N-vagljE8aVRGyrS1L0438d6TCY/s400/3+-+next+prayer+1.png" width="400" /></a><br />
<br />
<h3 style="text-align: left;">
Use fulfillment for fetching prayer times</h3>
<br />
To get to write some code to get the answer I need, I should enable fulfillments (enable webhooks checkbox). This is a special section where you can programmatically manipulate your agent. I also marked this intent as an end of conversation. This means that once I respond, the agent stops and does not listen to any more input from the user.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoi51KMXqh04S-fqcLmWLgOE6QgFYcNudZo8SEvrHO1lN8cuzO-BfLnSahdDRtFYxDk3ct04-U9lkcGUz-CDxDlyp7I1TD1YfGQzRF2oxz9ZtEqMDm0Xb_ztgaP3H-Qc7mbhvj1cMxc1c/s1600/3+-+next+prayer+2.png" imageanchor="1"><img border="0" height="181" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoi51KMXqh04S-fqcLmWLgOE6QgFYcNudZo8SEvrHO1lN8cuzO-BfLnSahdDRtFYxDk3ct04-U9lkcGUz-CDxDlyp7I1TD1YfGQzRF2oxz9ZtEqMDm0Xb_ztgaP3H-Qc7mbhvj1cMxc1c/s400/3+-+next+prayer+2.png" width="400" /></a><br />
<br />
I will get to the fulfillments section a bit later.<br />
<br />
<h2>
4 - Create a new intent for a specific prayer time</h2>
<br />
If I already can fetch and scan through some prayer times, creating the next intent was pretty easy in comparison to the previous one. That's because I includes no comparisons or calculations, but as simple as looking for a specific key in the prayers key-value object.<br />
<br />
<h3 style="text-align: left;">
Create a Prayer entity</h3>
<br />
To detect the prayer in the user's phrase, I had to create a special entity which does not exist by default, which is the prayer entity.<br />
<br />
This entity can be created from the left side panel. In the entity, I define the possible entries, along with any synonyms which can mean the same thing. So I added the names of the five prayers and added some synonyms for any different pronunciation.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNUP6y6YcdLQiQyd8KxQ4pNrK18hoRvY4Hggs_HNvVHMDq_hmqvW6Fmy-_5g8ZViTy598v_BJwqfbD6Ox7cyEJqeoZ2Bb_TNduDRHGai2K-S_1uDzC7dt0D45f36SYRg1cd2tuxSf2Y0A/s1600/4+-+prayer+entity.png" imageanchor="1"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNUP6y6YcdLQiQyd8KxQ4pNrK18hoRvY4Hggs_HNvVHMDq_hmqvW6Fmy-_5g8ZViTy598v_BJwqfbD6Ox7cyEJqeoZ2Bb_TNduDRHGai2K-S_1uDzC7dt0D45f36SYRg1cd2tuxSf2Y0A/s400/4+-+prayer+entity.png" width="400" /></a><br />
<br />
<h3 style="text-align: left;">
Create the intent with the phrases and responses like before</h3>
<br />
Just like the previous intent, I created a new one and added some training phrases and responses. And this time I added one more parameter: prayer. and I set its entity as Prayer, which I just created. And then I marked the prayer in each training phrase by marking the text and choosing the correct parameter from the list appearing.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlAEY-ZIupBq4Fs-qvLXhRaA4CntrwuyGRPKR0lkTp8t32NJ_YWBvAnZfRdYv9w9DqVqp1eDoimDqRhsWnJFwy7VGAkGLukDuVs8iDvXnGtejSb-xO9y2X_ERH9IfLK-uytdgDdJW2Edg/s1600/4+-+new+entity.png" imageanchor="1"><img border="0" height="192" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlAEY-ZIupBq4Fs-qvLXhRaA4CntrwuyGRPKR0lkTp8t32NJ_YWBvAnZfRdYv9w9DqVqp1eDoimDqRhsWnJFwy7VGAkGLukDuVs8iDvXnGtejSb-xO9y2X_ERH9IfLK-uytdgDdJW2Edg/s400/4+-+new+entity.png" width="400" /></a><br />
<br />
<br />
<h3 style="text-align: left;">
Enable fulfillment and end of conversation</h3>
<br />
This step is similar to the previous entity. I enabled fulfillments and marked the intent as end of conversation.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7UPPCp7Jsw2l0PBCGuykQS6nbeP8GvBjSd7k5y31Dmbuf-GUm3YV6X0tjmd37Fte4hIRKqUD-ZXi_kNd8m_DZp6W4GgJ1oVxf4IHXvb1NXP7VVcPAWdyQZm-4huY_kZE6Zf_nT7DyIss/s1600/4+-+entity+enable.png" imageanchor="1"><img border="0" height="191" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7UPPCp7Jsw2l0PBCGuykQS6nbeP8GvBjSd7k5y31Dmbuf-GUm3YV6X0tjmd37Fte4hIRKqUD-ZXi_kNd8m_DZp6W4GgJ1oVxf4IHXvb1NXP7VVcPAWdyQZm-4huY_kZE6Zf_nT7DyIss/s400/4+-+entity+enable.png" width="400" /></a><br />
<br />
<br />
<h2 style="text-align: left;">
5 - Fulfillment section</h2>
<br />
Now comes the programmatic part. In this section, we should enable the inline editor. This is where we write handlers to do what we want. There is another option to have webhooks written with some standards. But lets stick to the quick win of scripting something. Remember that you need a premium Firebase account to do this step to be able to call external APIs.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWrLwP2qdMUq71_X1msf2nQDdyC-JK2BiOOFqL3cLvEelDMGpX3CSXtSqdxzYKDwcWR_tcIOrrnCStbEAiBkJBtCf7fK1WIe7qS_umhXMm4UQmScd9VkUcudY6PhfskcRgxl0rjyJzHi8/s1600/5+-+fulfillment+enable.png" imageanchor="1"><img border="0" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWrLwP2qdMUq71_X1msf2nQDdyC-JK2BiOOFqL3cLvEelDMGpX3CSXtSqdxzYKDwcWR_tcIOrrnCStbEAiBkJBtCf7fK1WIe7qS_umhXMm4UQmScd9VkUcudY6PhfskcRgxl0rjyJzHi8/s400/5+-+fulfillment+enable.png" width="400" /></a><br />
<br />
The code here is as simple as writing a handler and registering this handler to a specific intent by matching its name. So the skeleton of my code looks like this:<br />
<br />
<pre class="brush:js">function nextPrayerTimeHandler(agent) {
// some code here
agent.add(`This is something the agent is going to say to the user!`);
}
function specificPrayerTimeHandler(agent) {
// some code here
agent.add(`This is something the agent is going to say to the user!`);
}
intentMap.set('NextPrayerIntent', nextPrayerTimeHandler);
intentMap.set('SpecificPrayerIntent', specificPrayerTimeHandler);
</pre>
<br />
<br />
This skeleton blends with the existing boilerplate in the inline editor. The content of my functions is where magic happens. I wrote some quick and dirty code based on the first API I found in my search for prayer times APIs. And spent a good amount of time fiddling with the rabbit hole of JS date objects and local vs UTC mess.<br />
<br />
Here is my code if you are curious or want to make your own cleaner code :)<br />
<br />
<pre class="brush:js">const https = require("https");
const apiBaseTodayPrayers = 'https://api.pray.zone/v2/times/today.json?higher-latitudes=3&school=3&city=';
const prayerKeys = ['Fajr', 'Dhuhr', 'Asr', 'Maghrib', 'Isha'];
var city = '';
var prayer = '';
function constructTimestampFromData(date, time, offset) {
var offsetSign = (offset > 0) ? '+' : '-';
var fullTimeString = `${date} ${time} GMT${offsetSign}${offset}`;
return (new Date(fullTimeString).getTime());
}
function findNextPrayerTimeFromJsonResponse(jsonData) {
let allPrayerTimes = jsonData.results.datetime[0].times;
let date = jsonData.results.datetime[0].date.gregorian;
let offset = jsonData.results.location.local_offset;
// Filter prayer times and convert from string to timestamp
var filteredPrayerTimes = {};
for (var timeKey in allPrayerTimes) {
if (prayerKeys.includes(timeKey)) {
filteredPrayerTimes[timeKey] = constructTimestampFromData(date, allPrayerTimes[timeKey], offset);
}
}
// Get local time
let now = new Date().getTime();
// Find the next prayer
var nextPrayerName = '';
var nextPrayerTime = '';
for (var key in filteredPrayerTimes) {
if (filteredPrayerTimes[key] > now) {
return [key, allPrayerTimes[key]];
}
}
}
function nextPrayerTimeHandler(agent) {
// Get the current city
city = agent.parameters.city;
// Make API call to fetch prayer times
const todayPrayersUrl = apiBaseTodayPrayers + city;
return new Promise((resolve, reject) => {
https.get(todayPrayersUrl, function(resp) {
var json = "";
resp.on("data", function(chunk) {
json += chunk;
});
resp.on("end", function() {
let jsonData = JSON.parse(json);
let nextPrayerNameAndTime = findNextPrayerTimeFromJsonResponse(jsonData);
// Respond
if (nextPrayerNameAndTime[0] === '') {
agent.add(`There are no more prayers in ${city} today.`);
} else {
agent.add(`The next prayer in ${city} is ${nextPrayerNameAndTime[0]} at ${nextPrayerNameAndTime[1]}.`);
}
resolve();
});
});
});
}
function specificPrayerTimeHandler(agent) {
// Get the current city
city = agent.parameters.city;
// Get the prayer name
prayer = agent.parameters.prayer;
const todayPrayersUrl = apiBaseTodayPrayers + city;
return new Promise((resolve, reject) => {
https.get(todayPrayersUrl, function(resp) {
var json = "";
resp.on("data", function(chunk) {
json += chunk;
});
resp.on("end", function() {
let jsonData = JSON.parse(json);
let prayerTime = jsonData.results.datetime[0].times[prayer];
// Respond
if (prayerTime === undefined) {
agent.add(`Sorry, I cound not find ${prayer} time for ${city}.`);
} else {
agent.add(`${prayer} in ${city} is at ${prayerTime}.`);
}
resolve();
});
});
});
}
intentMap.set('NextPrayerIntent', nextPrayerTimeHandler);
intentMap.set('SpecificPrayerIntent', specificPrayerTimeHandler);
</pre>
<br />
<br />
<h2 style="text-align: left;">
6 - Testing with Google Assistant</h2>
<br />
On the right side of the screen, there is a message saying;<br />
<blockquote class="tr_bq">
<div aria-disabled="false" class="ga-integration-test ng-scope layout-row" layout="row" ng-class="{'narrow': vm.hasSpeakResult}" ng-click="vm.onTestClick()" ng-disabled="vm.isPublishing()" ng-if="vm.state.isPermissionsSufficient" role="button" style="box-sizing: border-box; cursor: pointer; display: flex; flex-direction: row; outline: none; padding: 30px 0px;" tabindex="0">
<div class="b_integration_description" style="box-sizing: border-box; margin-left: 15px; outline: none;">
<div class="ng-scope" ng-if="!vm.isPublishing()" style="box-sizing: border-box; outline: none;">
<div class="ng-scope" ng-if="vm.areAllFieldsFilled()" style="box-sizing: border-box; outline: none;">
See how it works in <span style="box-sizing: border-box; color: #039be5; outline: none;">Google Assistant</span>.</div>
</div>
</div>
</div>
</blockquote>
Click it and you will be directed to Actions Console where you can test against a simulator for Google Assistant as well as other Google devices. You can also test on your own Google Home if you are connected to the same account from Google Home.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja99d991eSbcm2u68XhENvLMcnHk5T8iSyUupoKiVT-Sxkw0kB1hndUBTjs-JgsRraNim__8I3pGEr4BeJ4HpRm0Os_V8YElKiDTosLQLKtqEhK1X2HBTrnjJ5uNcBKKSrckoW4xcoGxQ/s1600/1+-+test.png" imageanchor="1"><img border="0" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja99d991eSbcm2u68XhENvLMcnHk5T8iSyUupoKiVT-Sxkw0kB1hndUBTjs-JgsRraNim__8I3pGEr4BeJ4HpRm0Os_V8YElKiDTosLQLKtqEhK1X2HBTrnjJ5uNcBKKSrckoW4xcoGxQ/s400/1+-+test.png" width="400" /></a><br />
<br />
And from the Actions Console you can modify some settings and deploy your app to alpha and beta versions, or even submit it to be reviewed and publicly published.<br />
<br />
<h2 style="text-align: left;">
Live demo</h2>
<br />
That's it! I created a simple app to help me with my daily need. And I learned something new along the way. I have more plans to improve my project to my needs and maybe publish it later to others.<br />
<br />
I hope you learned something from my post. :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/LpHutu9eK9g/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/LpHutu9eK9g?feature=player_embedded" width="320"></iframe></div>
<br />
<br />
<br />
<h2 style="text-align: left;">
Resources</h2>
<br />
<ul style="text-align: left;">
<li>LinkedIn Learning course: <a href="https://www.linkedin.com/learning/building-chatbots-using-google-dialogflow/" target="_blank">Building Chatbots Using Google Dialogflow</a></li>
<li><a href="https://prayertimes.date/api" target="_blank">Prayer times API</a></li>
</ul>
</div>
Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-308361232795615929.post-90590916424186020782020-04-01T19:10:00.000+02:002020-04-01T19:13:01.492+02:00Retry ruby code with exceptions multiple times<div dir="ltr" style="text-align: left;" trbidi="on">
This problem I faced is pretty simple. A method calling an external API or resource which might throw a random error or fail intermittently. So I want to simple retry because this error is ignorable if it happens one or two times.<br />
<br />
Now the standard ruby's <span style="font-family: "courier new" , "courier" , monospace;">retry</span> and <span style="font-family: "courier new" , "courier" , monospace;">redo</span> keywords are not helping here. Because the <span style="font-family: "courier new" , "courier" , monospace;">retry</span> will keep retrying endlessly and I want to raise the error after a few retries, and the <span style="font-family: "courier new" , "courier" , monospace;">redo</span> requires a flag to make it stop. So my solution to retry my ruby code for a few times if a specific exception happens was as simple as a method with the <span style="font-family: "courier new" , "courier" , monospace;">retry</span> logic and accepting a block to keep my code cleaner. And whenever I need to retry a not-so-reliable API, I wrap my code in this method as a block and choose the exception to <span style="font-family: "courier new" , "courier" , monospace;">retry</span> when it happens and how many times to retry.<br />
<br />
<pre class="brush:ruby"># whatever method I am using
def do_something_with_tolerance_to_errors
response = with_error_retry(WhateverException, 2) do
# my code goes here
end
end
# my retry handler
def with_error_retry(error_class, retries_count)
yield
rescue error_class => e
@retries ||= 0
raise e if @retries >= retries_count
@retries += 1
retry
end
</pre>
<br />
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-23791347034587619272020-03-26T18:31:00.000+02:002020-03-26T18:32:33.090+02:00How investigate a failed Docker build<div dir="ltr" style="text-align: left;" trbidi="on">
While working on a recent task, I got stuck with a Dockerfile which was failing to build. The failing container was failing at one step of building with native extensions of some Ruby gems. I wanted get into the container to see what the logs were saying.<br />
<br />
This is the code snippet I used to do so:<br />
<br />
Step 1: Get the container ID<br />
<br />
by running<br />
<br />
<pre class="brush:bash">$ docker ps -a
</pre>
<br />
I could look for the <span style="color: #242729; font-family: "consolas" , "menlo" , "monaco" , "lucida console" , "liberation mono" , "dejavu sans mono" , "bitstream vera sans mono" , "courier new" , monospace , sans-serif; font-size: 13px; font-style: inherit; font-weight: inherit; white-space: inherit;">CONTAINER ID</span> of the failing container.<br />
<br />
Step 2: Commit container to an image<br />
<br />
Using the container ID from the previous step, commit the container to create an image with its current state<br />
<pre class="brush:bash">$ docker commit ca73ac766f36
sha256 5b4586632f690964d3a2e2b958cf6d211bc924e47e80a9bc4a84c53779f39f79
</pre>
<br />
Step 3: Run the image<br />
<br />
Using the sha256 code from the previous step, run the image with bash and debug the image however you want. You don't have to use the full sha256 string<br />
<br />
<pre class="brush:bash">$ docker run -it 5b4586632f [bash -il]
</pre>
<br />
And that's it!</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-19264149074964638902018-10-01T17:56:00.000+02:002019-10-20T18:03:02.231+02:00Week 7 Reloaded - Fine-tuning VGG16 model for CIFAR-10 dataset<div dir="ltr" style="text-align: left;" trbidi="on">
Week 7 is the last week of this deep-learning plan. And I didn’t want to finish before trying a bit more to have better results. So here we go again.<br />
<br />
This time I learned two things: Google Colaboratory and Lambda layer. Those two have helped me refactor my code in two notebooks to get better results.<br />
<br />
<h3 style="text-align: left;">
Google Colaboratory</h3>
Because my computer was so slow to process such dataset, I didn’t have the luxury of experimenting with models and hyperparameters. So one obvious step was to look for an online solution to process my python code with an acceptable speed. Google Colaboratory was a nice and free solution that fitted my needs.<br />
<br />
<div style="color: #666666; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; letter-spacing: 0.4px; line-height: 1.7; margin-bottom: 20px;">
<span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));">If you haven’t used it before, here is a <a href="https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5d" style="color: indigo;">nice tutorial</a></span></div>
<div>
<div>
This processing power allowed me to rerun a previous notebook to train my own CNN for CIFAR-10 dataset. But for 100 epochs instead of only 15. This allowed me to see how my model was performing well enough to reach an accuracy of 82.68%. (compared to 74.53% with 15 epochs)</div>
<div>
<br /></div>
</div>
<div>
<div style="color: #666666; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; letter-spacing: 0.4px; line-height: 1.7; margin-bottom: 20px;">
<span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));">You can find the whole python notebook for my own model on Github <a href="https://github.com/MahmoudAdly/deeplearning-homework/blob/master/notebooks/cnn_cifar10_v2.ipynb" style="color: indigo;">here</a>.</span></div>
</div>
<div>
<h3 style="text-align: left;">
Fine-tuning VGG16 model</h3>
<div>
Now that I had Google Colaboratory, I could try a huge number of changes in different hyperparameters and layers customization. After a couple of days of trying, I could get a maximum accuracy of 73.33%. (compared to 68.03% with less tinkering).</div>
<div>
<br /></div>
<div>
This was the maximum test accuracy I could get. After that, the model was just overfitting to reach 90 something % training accuracy and no test accuracy improvement. I could go further and inject normalization and dropout layers. But that was enough for me for that part of my homework.</div>
<div>
<br /></div>
<div>
I could also solve my problem of not having enough memory to resize all the data and having to use part of it. This was possible by using a <span style="font-family: "courier new" , "courier" , monospace;">Lambda </span>layer as an input and use it to <span style="font-family: "courier new" , "courier" , monospace;">Reshape </span>every batch. Another solution was to use ImageDataGenerator. But I didn’t have to use it for now.</div>
<div>
<br /></div>
</div>
<div>
<div style="color: #666666; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; letter-spacing: 0.4px; line-height: 1.7; margin-bottom: 20px;">
<span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));">You can find the whole python notebook for a customization of VGG16 on Github <a href="https://github.com/MahmoudAdly/deeplearning-homework/blob/master/notebooks/transfer_learning_vgg16_cifar10_v2.ipynb" style="color: indigo;">here</a>.</span></div>
</div>
<div>
<br /></div>
<div>
<h3 style="text-align: left;">
What’s next</h3>
<div>
Instead of spending more time tinkering with an existing model to reach a result that I could reach easier with my own model, I thought it is not worth it. Of course I already achieved the purpose of this homework to recycle an existing model and adapt it to my needs. Now it was time to move on.</div>
<div>
<br /></div>
<div>
Next is to find a real-world problem and work on it with real-world data to achieve an acceptable solution. And this new solution should not only be a quick and dirty bunch of python files, but rather a proper project with coding standards and a user-friendly interface. This way, I can learn even more and sharpen my skills.</div>
<div>
<br /></div>
</div>
<br />
Resources:<br />
<ul style="text-align: left;">
<li><a href="https://blog.slavv.com/37-reasons-why-your-neural-network-is-not-working-4020854bd607">37 Reasons why your Neural Network is not working</a></li>
<li><a href="http://forums.fast.ai/t/vgg16-fine-tuning-low-accuracy/3123">VGG16 fine tuning: low accuracy</a></li>
<li><a href="https://stackoverflow.com/questions/42260265/resizing-an-input-image-in-a-keras-lambda-layer">Resizing an input image in a Keras Lambda layer</a></li>
<li><a href="https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5d">Google Colab Free GPU Tutorial</a></li>
</ul>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-54228062051557740362018-09-23T17:50:00.000+02:002019-10-20T19:25:38.363+02:00Week 7 - Transfer learning with VGG16 model and CIFAR-10 dataset<div dir="ltr" style="text-align: left;" trbidi="on">
Here I come to the 7th and final week of the initial plan to start working with deep learning. This week’s assignment was to use an existing CNN to learn the CIFAR-10 dataset. The VGG16 model was chosen for me to try. It was a quite interesting assignment to find some problems I will probably be facing again in the future.<br />
<br />
Let’s talk about what I did this week.<br />
<br />
<div style="color: #666666; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; letter-spacing: 0.4px; line-height: 1.7; margin-bottom: 20px;">
<span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));">You can find the whole python notebook on Github <a href="https://github.com/MahmoudAdly/deeplearning-homework/blob/master/notebooks/transfer_learning_vgg16_cifar10.ipynb" style="color: indigo;">here</a>.</span></div>
<div>
<h3 style="text-align: left;">
My images are small</h3>
<span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));">
</span>
<br />
<div>
This was the first problem I faced. According to the <a href="https://keras.io/applications/#vgg16">Keras documentation</a>, the model takes a minimum size of 32px. But actually when I tried, it was a minimum of 48px. But my dataset had images of size 32px. So I had to resize the images. This turned out to take more time that expected. There were a few solutions for resizing such data. And to find a suitable and less complicated solution, it took me quite some time to read the documentation of each one and try it.<div>
<span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));"><br /></span></div>
<br /><h3 style="text-align: left;">
My computer is a humble one</h3>
Not only did I have to resize the images -which was the easy part with integers- but I also had to convert the data type to float32 and normalize the data. This resizing task was not that easy for my computer after the the conversion to float32. That’s why -and after many times of a dead ipython kernel- I ended up using only one third of the training data and converting the data in batches.<br /><br />The error was always:<br /><br /><blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;">Allocation of X exceeds 10% of system memory.</span></blockquote>
</div>
<span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));">
<div>
<div>
<br />
This was not even a large set of data, but I should start looking into learning about more optimized methods of processing such data.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
How to reuse the VGG16 model?</h3>
<div>
Now getting to the step of reusing the model, I faced the question of how to reuse it. There were 4 options depending on the data I have and the data the model used initially for training. You can learn more about the options here: <a href="http://cs231n.github.io/transfer-learning/">http://cs231n.github.io/transfer-learning/</a>.</div>
<div>
<br /></div>
<div>
For my case, I assumed that the CIFAR-10 data is similar enough to the ImageNet data used for training the VGG16 model. So I used the model as a fixed feature extractor and I only added the fully connected layers at the end to classify 10 classes instead of 1000.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
The long wait for results</h3>
<div>
Now that I’ve solved the initial problems of preparing the data and the model, I started the training for the output layer of the model. This took about 4 hours. And after the wait, the test accuracy was 68% compared to 87.77% training accuracy. :( I already had a better accuracy last week (74.53%) using my own network.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
The sad end of the day</h3>
<div>
This was a sad result after a long time of waiting and restarting the computer a few times. But I believe that I should get used to this as a fundamental part of this data life. Now I have to find out the reason behind the result.</div>
<div>
<br /></div>
<div>
<ul style="text-align: left;">
<li><span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));"><div>
<div>
Was it overfitting related to the model?</div>
</div>
</span></li>
<li><span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));"><div>
<div>
Was it because the data was not enough?</div>
</div>
</span></li>
<li><span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));"><div>
<div>
Or was it because my images were smaller that what the network was trained for?</div>
</div>
</span></li>
<li><span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));"><div>
<div>
Do I have to fine-tune the model more?</div>
</div>
</span></li>
</ul>
</div>
<div>
Whatever the reason is, I will have to start by finding a quicker computer/cloud solution to make it easier to test any theory.</div>
<div>
<br /></div>
<div>
<h3 style="text-align: left;">
What’s next</h3>
<div>
The next step was to find a bigger real-world data set to play with it. But given the performance I had on my computer, my next step will be looking for an affordable cloud solution to train my models. This way I can put more time into learning and trying instead of spending half a day or even a whole day to test one theory. And I also want to investigate this not-so-large array resizing problem.</div>
<div>
<br /></div>
</div>
</div>
<div>
<br /></div>
</span></div>
Resources:<br />
<ul style="text-align: left;">
<li><a href="http://cs231n.github.io/transfer-learning/">CS231n Convolutional Neural Networks for Visual Recognition</a></li>
<li><a href="https://medium.com/@14prakash/transfer-learning-using-keras-d804b2e04ef8">Transfer Learning using Keras</a></li>
<li><a href="https://github.com/keras-team/keras/issues/4465">Using pre-trained VGG16 for another classification task</a></li>
<li><a href="https://keras.io/applications/#vgg16">Keras VGG16 application</a></li>
<li><a href="https://www.tensorflow.org/api_docs/python/tf/convert_to_tensor">tf.convert_to_tensor</a></li>
<li><a href="https://www.tensorflow.org/api_docs/python/tf/keras/backend/resize_images">tf.keras.backend.resize_images</a></li>
<li><a href="https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.concatenate.html">numpy.concatenate</a></li>
</ul>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-45479866130101683752018-09-11T17:49:00.000+02:002019-10-20T18:02:46.156+02:00Week 5/6 - Convolutional neural network (CNN) with CIFAR-10 dataset<div dir="ltr" style="text-align: left;" trbidi="on">
Here I am in Week 5&6 of my mentor’s plan to practice deep-learning and start solving real problems. This time, my homework was about designing another CNN like the previous week but with the CIFAR-10 dataset.<br />
<br />
At first, I was intimidated and thought that I really sucked and that I cannot really move on by myself. But Which I think was close to being true. :D But after several hours of looking closely at the dataset and reading the Keras documentation, I started to find results! Here they are.<br />
<br />
<span class="evidence" style="background-image: linear-gradient(rgba(39 , 243 , 106 , 0.15) , rgba(39 , 243 , 106 , 0.15)); color: #666666; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 16px; letter-spacing: 0.4px;">You can find the whole python notebook on Github <a href="https://github.com/MahmoudAdly/deeplearning-homework/blob/master/notebooks/cnn_cifar10.ipynb" style="color: indigo;">here</a>.</span><br />
<span style="color: #666666; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 16px; letter-spacing: 0.4px;"></span><br style="color: #666666; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; letter-spacing: 0.4px;" />
<span class="evidence" style="background-image: linear-gradient(rgba(39 , 243 , 106 , 0.15) , rgba(39 , 243 , 106 , 0.15)); color: #666666; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 16px; letter-spacing: 0.4px;">UPDATE: I reran the code but for 100 epochs and could reach a <span style="color: black;">82.68%</span> accuracy. The notebook is found <a href="https://github.com/MahmoudAdly/deeplearning-homework/blob/master/notebooks/cnn_cifar10_v2.ipynb" style="color: indigo;">here</a>.</span><br />
<br />
The accuracy I could get this time compared to previous MNIST homework reminded me of the difference between my 9X% results at school compared to the embarrassing university’s results :D<br />
<br />
Anyways. I could get an accuracy of 74.53%. This already required a lot of time to train on my humble computer with no GPU (I think about 2 hours). That’s why I only have one model in my notebook this time. That’s because it took a lot of time to test a single model, so I decided to try something crazy; waiting for only one epoch to finish and looking at the resulting accuracy. If it was not so promising, I stop the process and try with a different design and so on. At the end, I decided to let the current one proceed till the end and see the result.<br />
<br />
After reaching this number, I thought it is time to look online at how people solve such a problem. And then I found that link: <a href="https://github.com/BIGBALLON/cifar-10-cnn#accuracy-of-all-my-implementations">https://github.com/BIGBALLON/cifar-10-cnn#accuracy-of-all-my-implementations</a><br />
<br />
That person could also reach an accuracy of 76.27% at first with -I think- a known network that noobs like me don’t know yet. Then he had to go for more complicated and more famous networks to get much better results. Of course, the training time with a GPU was so scary; going for a day or even two.<br />
<br />
<h3 style="text-align: left;">
What’s next</h3>
<div>
<br /></div>
<div>
<div>
The conclusion of this week actually proves how good the plan I am following is. Because now I can see how complicated and time consuming it is to solve such a problem with small images and only 10 classes. That’s why next week of the plan is “transfer learning”. So I have to use an existing network (VGG16) and adapt it to my dataset to have better results.</div>
<div>
<br /></div>
</div>
<div>
<br /></div>
<div>
<div>
Resources:</div>
<div>
<ul style="text-align: left;">
<li><a href="https://keras.io/getting-started/sequential-model-guide/" target="_blank">Getting started with the Keras Sequential model</a></li>
<li><a href="https://keras.io/models/sequential/" target="_blank">The Sequential model API</a></li>
<li><a href="https://keras.io/datasets/#cifar10-small-image-classification" target="_blank">Keras CIFAR-10 documentation</a></li>
<li><a href="https://keras.io/layers/convolutional/#conv2d" target="_blank">Keras Conv2D documentation</a></li>
<li><a href="https://www.youtube.com/channel/UC4UJ26WkceqONNF5S26OiVw/playlists" target="_blank">deeplizard on Youtube</a></li>
</ul>
</div>
</div>
<div>
<br /></div>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-24782213228362417992018-09-08T17:41:00.000+02:002019-10-20T18:02:39.973+02:00Week 2/3/4 - Convolutional neural network (CNN) with MNIST dataset<div dir="ltr" style="text-align: left;" trbidi="on">
Week 2 of my deep learning plan was to train a convolutional network using the MNIST dataset. The intent is to learn the basics of convolutional networks. Instead of writing the code of my homework here again, I will only link to it on Github and speak about my experience with it here.<br />
<br />
<div style="color: #666666; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; letter-spacing: 0.4px; line-height: 1.7; margin-bottom: 20px;">
<span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));">You can find the whole python notebook on Github <a href="https://github.com/MahmoudAdly/deeplearning-homework/blob/master/notebooks/cnn_mnist.ipynb" style="color: indigo;">here</a>.</span></div>
This homework was the fist step of feeling not like the hello-world example in week 1. The first step was to load the MNIST data like before, analyzing the data and curating it before processing. It was not complicated but it was a nice start to realizing how important this step is for deciding how to design the network.<br />
<br />
Next was the mix of confusion and fun. It was basically about looking for the reasonable number of hidden layers and number of filters per layer. After finding the ‘popular’ numbers for such a problem (what if my problem is a personal/custom one? back to that later in the future inshallah), it was time to try differt depths and different values for hyperparameters like dropout and filters. This was actually a boring and a time consuming step. Just to try some basic variations, I had to keep my computer running for about 4 hours. And I didn’t even try so many variations (only 7 variations).<br />
<br />
The results were fine and I could reach an accuracy of 99.32% compared to 98.48% in my previous feedforward assignment.<br />
<br />
<h3 style="text-align: left;">
Week 3 & 4</h3>
<div>
<div>
<br />
Week 3 & 4 were actually about playing with hyperparameters and optimization. So it was somehow included in my current assignment. I could get a taste of how they can affect the results and how I should give a considerable time, changing and observing the effects of such parameters and deciding which parameters can introduce an accuracy improvement.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Resources<br />
<ul style="text-align: left;">
<li><a href="https://keras.io/layers/convolutional/" target="_blank">Keras documentation</a></li>
<li><a href="https://www.youtube.com/channel/UC4UJ26WkceqONNF5S26OiVw/playlists" target="_blank">deeplizard on Youtube</a></li>
</ul>
</div>
<div>
<br /></div>
</div>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-45131209017602251662018-09-08T17:33:00.000+02:002019-10-20T18:02:34.753+02:00Week 1 - Feedforward network with MNIST dataset<div dir="ltr" style="text-align: left;" trbidi="on">
Week 1 of the plan was to train a feedforward network using the <a href="https://keras.io/datasets/#mnist-database-of-handwritten-digits" target="_blank">MNIST dataset</a>. This is probably the easiest and most straightforward example to understand how the training cycle goes.<br />
<br />
Before starting, I should mention that the code for this week -and the other weeks too- was not written from scratch by me. And that’s the main difference between school and work. It treated this homework the same way I treat work. I can google what I want and understand/refactor it. It can also be about reading about a specific network and understanding the recommended range of values for a specific parameter or the recommended layers structure.<br />
<br />
This time, it was too simple that I copied the code and started playing with it to understand how it works. And as we move forward in the weeks, I had to write more myself. Consider it a Hello World week. Now lets see the code!<br />
<br />
<div style="-webkit-text-stroke-width: 0px; color: #666666; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 300; letter-spacing: 0.4px; line-height: 1.7; margin-bottom: 20px; orphans: 2; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;">
<span class="evidence" style="background-image: linear-gradient(rgba(39, 243, 106, 0.15), rgba(39, 243, 106, 0.15));">You can find the whole python notebook on Github <a href="https://github.com/MahmoudAdly/deeplearning-homework/blob/master/notebooks/feedforward_mnist.ipynb" style="background-color: transparent; color: indigo; text-decoration: underline;">here</a>.</span></div>
Resources:<br />
<br />
<ul style="text-align: left;">
<li>Victor Schmidt (Vict0rSch) on <a href="https://github.com/Vict0rSch/deep_learning/blob/master/keras/feedforward/feedforward_keras_mnist.py">Github</a> </li>
<li>deeplizard on <a href="https://www.youtube.com/channel/UC4UJ26WkceqONNF5S26OiVw/playlists">Youtube</a></li>
</ul>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-73025956173275009012018-09-08T17:14:00.000+02:002019-10-20T19:26:05.301+02:00Week 0 - Jupyter notebook with Keras and Tensorflow<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
Starting with my deep-learning learning plan, I wanted to have a good development environment. This meant having a Docker image that can work on any device I am using with no extra setup needed. My choice was to use Keras with Tensorflow core for an easy start with not so many unwanted details at this step. I also chose to use Jupyter notebook to have a nice interface to trace/explain my code along with graphs and output numbers.<br />
<br />
The following is my finalized Dockerfile with the latest versions which worked exactly how I wanted it.<br />
<br />
<br /></div>
<pre class="brush:plain"># To build the container
# docker build -t jupyter-keras .
# To run the container:
# docker run -it -v /$(pwd)/:/home/jovyan/work -p 8888:8888 jupyter-keras:latest start-notebook.sh --NotebookApp.token=''
# To access the notbook from the browser:
# http://localhost:8888/tree
# To login in to the server:
# docker exec -it <container id=""> /bin/bash
# To check Keras version:
# python -c 'import keras; print(keras.__version__)'
FROM jupyter/scipy-notebook
MAINTAINER Gaarv <@Gaarv1911>
USER root
# bash instead of dash to use source
RUN ln -snf /bin/bash /bin/sh
USER jovyan
RUN pip install --upgrade pip \
&& pip install --upgrade tensorflow \
&& pip install --upgrade --no-deps git+git://github.com/keras-team/keras.git \
&& pip install --upgrade --no-deps h5py
</pre>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-20259397024348447842018-09-04T17:03:00.000+02:002019-10-20T18:01:39.271+02:00A deep-learning plan from a mentor<div dir="ltr" style="text-align: left;" trbidi="on">
I’ve been learning machine learning by myself for a long time. From one Coursera specialization to a course to a YouTube playlist. But then I felt the problem with starting. I am learning the theory with some basic applications, but I don’t know how to go on by myself and start a project and analyze the data and find the correct structure and find-tune the parameters and so on…<br />
<br />
Then came to me the old idea one more time; I need a mentor who knows how such professional life works and what really matters more. And after some searching and asking, I found one through a friend of a friend of a friend. And then she contacted me and offered help.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="555" src="https://www.linkedin.com/embed/feed/update/urn:li:share:6394120458446340096" width="504"></iframe><br />
<br />
And after explaining to her what I know and what I want in life from this exercise, she formulated a plan for me. So here I am posting it.<br />
<br />
<h3 style="text-align: left;">
Week 1: Feedforward networks</h3>
<div>
<br /></div>
<div>
<div>
A good start is the simple MNIST dataset. So train a feedforward network to study the basics of neural networks.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Week 2: Convolutional networks</h3>
<div>
Change the previous network to a convolutional network to study the basics of convolutional network.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Week 3: Hyperparameter optimization</h3>
<div>
Change the number of layers and different learning rates and other hyperparameters to learn validation and hyperparameter optimization.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Week 4: Dropout and batch normalization</h3>
<div>
Introduce dropout and batch normalization to the network to learn regularization and the rest of hyperparameter optimization. Up till now, it’s learning deep learning basics rather than a project.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Week 5&6: CIFAR-10 dataset</h3>
<div>
Repeat the above classification project but switch to CIFAR-10 dataset. There might be a few changes in the process, like input normalization and the need for more conv layers and so on. But this should solidify the knowledge.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Week 7: Transfer learning</h3>
<div>
Since you won’t train on your own from scratch all the time (no time and no resources), we sometimes borrow the lower layers from pre-trained networks and refine them. Use VGG model parameters with any other dataset and retrain for fine-tuning .</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Which tools to use?</h3>
<div>
Since all the project is deep learning stick to Tensorflow and Keras, Keras is way easier but it is not as flexible. So you may want to choose between them based on your end goal. But give both a try and make sure you understand the basics in a theoretical level first. Consult a tutorial or a book or whatever you’re comfortable with. There are tons of materials online. Stanford course is one. It is more academic but easy. You won’t need other libraries, like scikit unless you want to play and compare with other algorithms or perhaps do a little input manipulation via them.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
What’s next?</h3>
<div>
I assume if you reached this level, you’d have a pretty decent knowledge with clean data. I’ll look into other datasets to play with, as I know people who don’t consider the standard research datasets as a project but rather more of a tutorial following. So when you add it to your CV, it’ll look better.</div>
</div>
<div>
<br /></div>
<div>
<hr />
<div>
<br />
I was almost done with all the steps and then got busy in life once again. Now I wanted to start the engines again and move on to a personal project to learn more, but I have some solid doubts that I did not do a 100% clean homework. That’s why I wanted to post my solution for every week again after revising it, cleaning it, and making sure that I can understand and present it well.</div>
<div>
<br /></div>
</div>
<div>
<br /></div>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-65872793211744591532016-05-02T11:07:00.000+02:002016-05-02T11:10:55.056+02:00Download All Images from a Wordpress.com Media Library<div dir="ltr" style="text-align: left;" trbidi="on">
After a few months of blogging photos from my life events, I realised that I should have kept a local version (dummy me). I kept everything else, so why did I forget this time?!<br />
Anyways, I realised that this is not an easy job if you are free hosting on Wordpress.com. All I could have is either an XML with posts and references to images, or a full migration to another Wordpress blog.<br />
So I rolled up my sleeves and reused a python script I used before.<br />
<br />
Here is the script:<br />
<pre class="brush:python">#!/usr/bin/python
# -*- coding: utf-8 -*-
# This script parses a wordpress backup XML and downloads the contained media files.
# Example:
# python wordpress-media-downloader.py /Users/madly/Downloads/mycoolblog.wordpress.2016-05-02.xml /Users/madly/Downloads/media-library
import sys
import urllib2
import re
#
# Takes a url and a directory for saving the file. Directory must exist.
#
def download(url, dir_name):
file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(dir_name+'/'+file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading File: %s (Size: %s Bytes)" % (file_name, file_size)
file_size_dl = 0
block_sz = 8192
while True:
buffer = u.read(block_sz)
if not buffer:
break
file_size_dl += len(buffer)
f.write(buffer)
status = r"%10d [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
status = status + chr(8)*(len(status)+1)
print status,
f.close()
#
# Take file name and directory parameters from the user call
#
file_name = sys.argv[1]
dir_name = sys.argv[2]
img_regex = '(http:\/\/.*files\.wordpress\.com.*\.(?:jpeg|jpg|png))'
#
# Get the images urls from the xml file
#
urls_to_download = []
file = open(file_name, "r")
for line in file.readlines():
m = re.search(img_regex, line)
if m:
img_url = m.group(1)
urls_to_download.append(img_url)
urls_count = len(urls_to_download)
print("Images to download: %s images." % (urls_count))
#
# Download images
#
i = 0
for url in urls_to_download:
i += 1
print("File %s/%s" % (i, urls_count))
download(url, dir_name)
# print(url) # use this line instead of download() if you want to export the output to a download manager
</pre>
<br />
I simply call the script and pass the backup XML file I've just downloaded from my Wordpress.com admin panel (/wp-admin) and the path to the folder where I want to save the images.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdyoX2IkFFVlZF7cvViaxJ30WcTHAlmSF98mFSceqqG-E46rv2t5lvs79uLkUdR3zfwrhaicDgn9ZEUmL5NFF5P1IRg3bpVIwr3_e6zi7tKlVC06MVEIl5fidP6TVG7gsKeBiKMHTTkb8/s1600/Screen+Shot+2016-05-02+at+10.34.36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="80" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdyoX2IkFFVlZF7cvViaxJ30WcTHAlmSF98mFSceqqG-E46rv2t5lvs79uLkUdR3zfwrhaicDgn9ZEUmL5NFF5P1IRg3bpVIwr3_e6zi7tKlVC06MVEIl5fidP6TVG7gsKeBiKMHTTkb8/s400/Screen+Shot+2016-05-02+at+10.34.36.png" width="400" /></a></div>
<br />
<br />
One more update can be to use a better parsing technique and use an element called <i>wp:post_id</i> to sort the images into folders by their post. But I am satisfied with the current result. I can do the rest manually as it will be faster in my case :)<br />
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-58447203325066133772016-05-01T11:19:00.003+02:002017-03-23T10:39:17.293+02:00PENNY (Congstar) USB Internet on Linux Mint 17 - Ubuntu 14.04<div dir="ltr" style="text-align: left;" trbidi="on">
So, I've been in Germany for a while. And after using the fast WiFi in hotels, I moved to a personal apartment, with no Internet for now. I bought a PENNY USB surf stick as I thought it is a bit cheaper. But there was a problem, it is not working on Linux :(.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO3wzIOrgG3ASWhGLcwVsucIHIp7y4mUYxC5AVKjKwwUY8_5r6Mg30uQQsNbxUk4pVOt9P3yHhGPXsMsLrkG0B8UKUbh_RAqQOzXbejh32sLHH6dk6KYVDK0L5E_earJcy17W4AFapFI4/s1600/penny-stick-aktion-1b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO3wzIOrgG3ASWhGLcwVsucIHIp7y4mUYxC5AVKjKwwUY8_5r6Mg30uQQsNbxUk4pVOt9P3yHhGPXsMsLrkG0B8UKUbh_RAqQOzXbejh32sLHH6dk6KYVDK0L5E_earJcy17W4AFapFI4/s320/penny-stick-aktion-1b.jpg" width="320" /></a></div>
<br />
I lived with this fact for a while, but now I want to get back to programming. So, after some searching and trying, here is how I could use my PENNY Mobil Internet Stick, without their Internet manager of course, and that's a bit bad for calculating my overall usage.<br />
<br />
Anyways, it is straight forward:<br />
<br />
1. Open 'Network Connections'. Click 'Add'.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm0R0gi5OoMk2Ffy9_fDJObHu91NpBRrUo5ptcSJ66Urtde-uNhY40MkADKKTGjh5_O3-avL41gM1CqVpwetpepTqVkQ7rc6U6HueyRzO88VTPe9sDHq5OBcui8uV-n58ZuDNCK9hn1jk/s1600/Screenshot+from+2016-05-01+10%253A20%253A58.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="325" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm0R0gi5OoMk2Ffy9_fDJObHu91NpBRrUo5ptcSJ66Urtde-uNhY40MkADKKTGjh5_O3-avL41gM1CqVpwetpepTqVkQ7rc6U6HueyRzO88VTPe9sDHq5OBcui8uV-n58ZuDNCK9hn1jk/s400/Screenshot+from+2016-05-01+10%253A20%253A58.png" width="400" /></a></div>
<br />
2. Choose 'Mobile Broadband'.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJomYG2a9xR2orsqrAqVAA-ANSUjzBg8SM18UhP1wekBNo1Sc1bDXHsHRrarGyWpSbsMlxZNyM3Wg4-Vx3WSQfMzSKoVOy0YnQfpS2RIWz6Gq4tKkowGXwoOIyq5cIu8i1S7kOzipW62k/s1600/Screenshot+from+2016-05-01+10%253A21%253A09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="171" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJomYG2a9xR2orsqrAqVAA-ANSUjzBg8SM18UhP1wekBNo1Sc1bDXHsHRrarGyWpSbsMlxZNyM3Wg4-Vx3WSQfMzSKoVOy0YnQfpS2RIWz6Gq4tKkowGXwoOIyq5cIu8i1S7kOzipW62k/s400/Screenshot+from+2016-05-01+10%253A21%253A09.png" width="400" /></a></div>
<br />
3. The default device is 'Internetstick'. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-3d0_4mNnB-ZH3Gg8LUcGPo7qCefPCwfrj0-IS4dsY7yXE2dgCaMOuh7jZfO785RzVSF61wyRWy9pVg4tGBZhalZdnGB3kM4Kg_H0Qbx1jN05FwSgeCRjJLGAafjfMlxIQjnaTgLpodM/s1600/Screenshot+from+2016-05-01+10%253A21%253A20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="157" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-3d0_4mNnB-ZH3Gg8LUcGPo7qCefPCwfrj0-IS4dsY7yXE2dgCaMOuh7jZfO785RzVSF61wyRWy9pVg4tGBZhalZdnGB3kM4Kg_H0Qbx1jN05FwSgeCRjJLGAafjfMlxIQjnaTgLpodM/s400/Screenshot+from+2016-05-01+10%253A21%253A20.png" width="400" /></a></div>
<br />
4. Choose your country. Mine is 'Germany'. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLjEK0BkvrnZYo_dnmShX2fhPEEWGsKWp28Gok3gkxlgOZyVqxf7kHhSRd4OvY2FLo6nxxR1lRPCGjf_NRScbKIWzNtxn7AvP1Y17z7sD8bDogV9wN91gslKWpIPdNf_MffsL1coZqvfA/s1600/Screenshot+from+2016-05-01+10%253A21%253A48.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="157" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLjEK0BkvrnZYo_dnmShX2fhPEEWGsKWp28Gok3gkxlgOZyVqxf7kHhSRd4OvY2FLo6nxxR1lRPCGjf_NRScbKIWzNtxn7AvP1Y17z7sD8bDogV9wN91gslKWpIPdNf_MffsL1coZqvfA/s400/Screenshot+from+2016-05-01+10%253A21%253A48.png" width="400" /></a></div>
<br />
5. The service provider is 'Congstar'. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHGAbtb2oALE-z2L3O9uprHmClEWEMhOVOihHTUhYXt2a9S75Qjhz5RnwnNuvuOaAGFx59L58TF6G1o9wRWwWshWc7WeYYPDNUlHGz-9k-IzTreVAYIIvChztwbOuPXrZt2I9o74ijHfY/s1600/Screenshot+from+2016-05-01+10%253A21%253A54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="157" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHGAbtb2oALE-z2L3O9uprHmClEWEMhOVOihHTUhYXt2a9S75Qjhz5RnwnNuvuOaAGFx59L58TF6G1o9wRWwWshWc7WeYYPDNUlHGz-9k-IzTreVAYIIvChztwbOuPXrZt2I9o74ijHfY/s400/Screenshot+from+2016-05-01+10%253A21%253A54.png" width="400" /></a></div>
<br />
6. The default plan is 'Prepaid Contracts', which is my case. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-XjtVlGuLAyzQQqdzamPteH7bmnHKPVSYEk28zY7gkitzR-_gxnvMBpyJyxbi7NyNiMmpCtFhwwK7q9qC4h_hSR39W1aFteUxVglCDwFmijenc8_d2OkR921seQWnhZaatDcUaQBG8Zc/s1600/Screenshot+from+2016-05-01+10%253A22%253A00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="157" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-XjtVlGuLAyzQQqdzamPteH7bmnHKPVSYEk28zY7gkitzR-_gxnvMBpyJyxbi7NyNiMmpCtFhwwK7q9qC4h_hSR39W1aFteUxVglCDwFmijenc8_d2OkR921seQWnhZaatDcUaQBG8Zc/s400/Screenshot+from+2016-05-01+10%253A22%253A00.png" width="400" /></a></div>
<br />
7. Verify your data and click 'Apply'. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5LzdquJX8HSA5F5kyUGZdNZdw8i4142vR0qL8QRAwycKdvVF7dl5RZ3aKJJ5xq7h9QyYe-Y_4mKpveFMTdDKSnDsm_TBC65AgdM_Rqm5KKW-ZXYInl1qwKFH6OO8GxKpGpGGObRn7LEg/s1600/Screenshot+from+2016-05-01+10%253A22%253A06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="157" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5LzdquJX8HSA5F5kyUGZdNZdw8i4142vR0qL8QRAwycKdvVF7dl5RZ3aKJJ5xq7h9QyYe-Y_4mKpveFMTdDKSnDsm_TBC65AgdM_Rqm5KKW-ZXYInl1qwKFH6OO8GxKpGpGGObRn7LEg/s400/Screenshot+from+2016-05-01+10%253A22%253A06.png" width="400" /></a></div>
<br />
8. The connection form is pre-populated with the correct data. I just changed the connection name to be meaningful to me. (and the password was tm)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg36JSaSkElb11BVQVn4qxdX-ftzd6avDIr_Cu4bSD7VB5bdamHutSFFuNNDt6ThBxnufuJA-XTaKi9rkVpb0yQrW5VTF-3uTiYER8bYeieUQHnBn6o9YMFmE1hEmmrr1JtawAxAU9oGj0/s1600/Screenshot+from+2016-05-01+10%253A22%253A29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg36JSaSkElb11BVQVn4qxdX-ftzd6avDIr_Cu4bSD7VB5bdamHutSFFuNNDt6ThBxnufuJA-XTaKi9rkVpb0yQrW5VTF-3uTiYER8bYeieUQHnBn6o9YMFmE1hEmmrr1JtawAxAU9oGj0/s400/Screenshot+from+2016-05-01+10%253A22%253A29.png" width="300" /></a></div>
<br />
9. Go to 'IPv4 Settings' tab, and Add this additional DNS server IP: 193.254.160.1. Now click 'Save'. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxnjhLCMxFG8ae7sBwKQ1L6XAxTq3EhGFhz7yF611YwNIvGcdse95_p2CDMfqagMchlsw9VeyWUPreYr-o14QLncZnY13IbGgAXQ-JTMxD_yP2CxAISRR9rgSDSJX4nGyFpcXGCsSFW6Q/s1600/Screenshot+from+2016-05-01+10%253A22%253A58.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxnjhLCMxFG8ae7sBwKQ1L6XAxTq3EhGFhz7yF611YwNIvGcdse95_p2CDMfqagMchlsw9VeyWUPreYr-o14QLncZnY13IbGgAXQ-JTMxD_yP2CxAISRR9rgSDSJX4nGyFpcXGCsSFW6Q/s400/Screenshot+from+2016-05-01+10%253A22%253A58.png" width="300" /></a></div>
<br />
10. The connection is added. Click 'Close'. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6k52LqHa1AMwNclDoUbAgofAkwPMgLiX79AnEEB7Mh5uPJdmyQLS7IC3c2l_P6clGt1dg7e1mH831OhvG1w5uggjIKg700qS1jYMUnQyBQlnurNvdBJnMp2QOgWllPWSVLMo33PL7RmY/s1600/Screenshot+from+2016-05-01+10%253A23%253A10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6k52LqHa1AMwNclDoUbAgofAkwPMgLiX79AnEEB7Mh5uPJdmyQLS7IC3c2l_P6clGt1dg7e1mH831OhvG1w5uggjIKg700qS1jYMUnQyBQlnurNvdBJnMp2QOgWllPWSVLMo33PL7RmY/s400/Screenshot+from+2016-05-01+10%253A23%253A10.png" width="400" /></a></div>
<br />
11. Now you can see in the connections list that a broadband section is added. Turn it on it was off. Or click the connection name you just created (Congstar Prepaid PENNY).<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm3SE9TncnccupKoSLjpaBcHQIzXDPEAIlAg_D0oAM4tVXIFq8xhbGkNWa8ggVAmaa-He_gEBwClw6Y1gO2TKnoXwXGKDHLi4MuBw5oy9-4SUPYcfOFL68v9xXXQkuLODtKYjD3U9slI0/s1600/Screenshot+from+2016-05-01+10%253A23%253A31+-+2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm3SE9TncnccupKoSLjpaBcHQIzXDPEAIlAg_D0oAM4tVXIFq8xhbGkNWa8ggVAmaa-He_gEBwClw6Y1gO2TKnoXwXGKDHLi4MuBw5oy9-4SUPYcfOFL68v9xXXQkuLODtKYjD3U9slI0/s400/Screenshot+from+2016-05-01+10%253A23%253A31+-+2.png" width="248" /></a></div>
<span id="goog_1724463887"></span><span id="goog_1724463888"></span><br />
Now you are connected. Enjoy the surfing and keep an eye on the System Monitor for data usage :).</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-37282659907057760312016-01-05T09:25:00.000+02:002016-01-05T09:25:20.217+02:00Ribbon Wordpress Theme - Arabic Version<div dir="ltr" style="text-align: left;" trbidi="on">
In case you don't know about <a href="https://mythemeshop.com/themes/ribbon/" target="_blank">Ribbon</a>, It is a free responsive WordPress magazine theme.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbkBIOwOzL8WPl5IRwTvCk8UYS-uES4N9xPrCPgiQ9ISJ965CixDM3zutw51EpAp2WDPPSle-d2RBswV4DEx11nc24OVEX60hNmHbNrnXtj6wGnT3gX37dSsNiW_p2PImxsERFjMLgRB4/s1600/Ribbon-590x295.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbkBIOwOzL8WPl5IRwTvCk8UYS-uES4N9xPrCPgiQ9ISJ965CixDM3zutw51EpAp2WDPPSle-d2RBswV4DEx11nc24OVEX60hNmHbNrnXtj6wGnT3gX37dSsNiW_p2PImxsERFjMLgRB4/s1600/Ribbon-590x295.png" /></a></div>
<br />
A few years ago, I used it for a website/blog which I used to use and maintain. To reach an appealing view, it was not enough to change direction to RTL or use an automated tool that used to exist back then. So I spent some time to translate some segments and style a few elements. The final state was as follows:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOIxp7nNq-PYr81LzG2p6lN1zuYdY0E1yRFw7mNIdFQ5mbFWqB4klRUdfpfBrasr5-fvKFfPVvVnpolhA0LCdkmDvbliIRJTJuCfPCF_XDhY1we7rmtuhgJ6_SGzBRp7vROdeCZanBI5E/s1600/ribbon-arabic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOIxp7nNq-PYr81LzG2p6lN1zuYdY0E1yRFw7mNIdFQ5mbFWqB4klRUdfpfBrasr5-fvKFfPVvVnpolhA0LCdkmDvbliIRJTJuCfPCF_XDhY1we7rmtuhgJ6_SGzBRp7vROdeCZanBI5E/s1600/ribbon-arabic.png" /></a></div>
<br />
Ribbon is a great Wordpress theme, and I really liked its performance and clean look. So, here it is in Arabic. I hope it would be useful for someone out there.<br />
<br />
<a href="http://www.mediafire.com/download/1d954qn2t26njyn/arabic_ribbon_enhanced.zip" target="_blank">Download Arabic Ribbon</a><br />
<br />
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-44825343167789531712015-12-18T20:21:00.001+02:002015-12-18T20:24:33.207+02:00Download RSS Media Files using Python<div dir="ltr" style="text-align: left;" trbidi="on">
Today I wanted to download a course from an RSS feed. The media files (mp3) were attached to the RSS entries. So instead of downloading tens of files one by one, I wrote a script!<br />
<br />
With the help of Alvin's post to <a href="http://alvinalexander.com/python/python-script-read-rss-feeds-database" target="_blank">parse RSS with Python</a>, I attached a download method and the script was ready to do the work for me.<br />
<br />
First, here is the script:<br />
[<i>rss_downloader.py</i>] <br />
<pre class="brush:python">#!/usr/bin/python
import feedparser
import sys
import urllib2
#
# Takes a url and a directory for saving the file. Directory must exist.
#
def download(url, dir_name):
file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(dir_name+'/'+file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading File: %s (Size: %s Bytes)" % (file_name, file_size)
file_size_dl = 0
block_sz = 8192
while True:
buffer = u.read(block_sz)
if not buffer:
break
file_size_dl += len(buffer)
f.write(buffer)
status = r"%10d [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
status = status + chr(8)*(len(status)+1)
print status,
f.close()
#
# Take url and directory parameters from user call
#
url = sys.argv[1]
dir_name = sys.argv[2]
#
# Get the feed data from the url
#
feed = feedparser.parse(url)
#
# Collect urls to download
#
urls_to_download = []
for entry in feed.entries:
links = entry.links
for link in links:
if link.type == u'audio/mpeg':
urls_to_download.append(link.href)
print("Files count: %s" % (len(urls_to_download)))
#
# Download files
#
for url in urls_to_download:
download(url, dir_name)
# print(url)
</pre>
<br />
You just call the script, pass RSS url, and the directory to save the files.<br />
<pre class="brush:bash">python rss_downloader.py http://rss.dw.com/xml/DKpodcast_dwn1_en /home/madly/DeutschWarumNicht/serie1
</pre>
<br />
This will parse the RSS feed, print the available links with type "audio/mpeg" (you can change this to be a value passed by the user), and download them with a progress display<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9_jS2IjJLQPnvJtulSo6-BTV-1abgcdx2OKtCbptiKCYwCehUmfdJQim5LxAow8fHgGVU-fuBNHhxlG9rUSAO6NFRSHEBegmQbOCfpVluNUbt8cYnIGObas9iK2QLrPEgEj8kj1q4mdA/s1600/download-progress.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="36" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9_jS2IjJLQPnvJtulSo6-BTV-1abgcdx2OKtCbptiKCYwCehUmfdJQim5LxAow8fHgGVU-fuBNHhxlG9rUSAO6NFRSHEBegmQbOCfpVluNUbt8cYnIGObas9iK2QLrPEgEj8kj1q4mdA/s640/download-progress.png" width="640" /></a></div>
This it. But there is more...<br />
<br />
After doing this, I realized that I could simply print the output, copy the links all together to my download manager as a batch download, and enjoy the features of my download manager. After all, I wanted to download the files, not make a full application. Dummy me :D <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGYgBnoLz446JYc8IdEouAc2r3F2b5lr9JlqHjxSL6xVCUOlsZbPQFHfZKKFffgJOxrm23IFka5zlQUpMJs2fQYH5CErql_p6NGTYz0u-E_qiNjBrf23O_rWdQXEQsowmHCh1S8qRrKIA/s1600/batch-download.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGYgBnoLz446JYc8IdEouAc2r3F2b5lr9JlqHjxSL6xVCUOlsZbPQFHfZKKFffgJOxrm23IFka5zlQUpMJs2fQYH5CErql_p6NGTYz0u-E_qiNjBrf23O_rWdQXEQsowmHCh1S8qRrKIA/s640/batch-download.png" width="640" /></a></div>
<br />
However you choose to go with the script, I hope you find it useful.<br />
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-10181627859243662172015-11-30T10:54:00.002+02:002015-12-06T12:23:15.317+02:00CSE 321 Project - Part 2 - Install Ruby, Rails, PostgreSQL, MySQL and others<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-size: small;"><i>This is one
post in a series of posts related to CSE 321 - Software Engineering
project for ASU CSE 2017 students. The series starts from setting up the
environment, to planning the work on the website project, to
implementation</i></span><br />
<br />
After Installing Lubuntu/Ubuntu in a VM in <a href="http://3adly.blogspot.com.eg/2015/11/cse-321-project-part-1-install-lubuntu.html" target="_blank">part 1</a>, we want to prepare it for development.<br />
<br />
Thinking about the required project, this is what I want to install.<br />
<ul style="text-align: left;">
<li>Ruby</li>
<li>Rails</li>
<li>PostgresSQL (or MySQL)</li>
<li>Node.js</li>
<li>Git</li>
<li>Gitg (a graphical tool to use with Git)</li>
<li>Atom editor (or you can install Sublime if you like)</li>
</ul>
Plus enabling copy & paste between the VM and the computer, and enabling flexible display resolution to suit your taste. And I will start with this.<br />
<br />
<span style="font-size: x-large;">Install Guest Additions</span><br />
<br />
Now we need to install some additional packages to add more power to our VM. This will make our job easier in scaling the VM window (and keeping the aspect ratio), and enables copy&paste between the VM and the host computer to easily follow the next instructions.<br />
<br />
If you have a toolbar in the outer VM window, go to <b>Devices</b> -> Insert Guest <b>Additions CD image...</b>. If you cannot find the toolbar, press <b>Right Ctrl + Home</b> buttons and the menu should appear. (if you still fail, press <b>Right Ctrl + C</b> and search for the toolbar again)<br />
<br />
Once you do this, a virtual CD will be mounted (inserted) into the machine to install some additional packages.<br />
<br />
In case of Ubuntu, the process should start immediately once you click <b>Run</b> in the box that will appear.<br />
<br />
In case of Lubuntu, you will have something like this:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEKtDz0adrGax60cUBZoC2McG9KE30TYlbCV2skASxkSHibKk4RvPVkEzjAFuR8_pFvrY4-ZyyxDkOlSCV9_3QY_UM3BRAIa0-4dsoIY_QPrNhCAbprDAWbrSdRSLr0HcYJUF0sc9f69I/s1600/vm-27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEKtDz0adrGax60cUBZoC2McG9KE30TYlbCV2skASxkSHibKk4RvPVkEzjAFuR8_pFvrY4-ZyyxDkOlSCV9_3QY_UM3BRAIa0-4dsoIY_QPrNhCAbprDAWbrSdRSLr0HcYJUF0sc9f69I/s640/vm-27.png" width="640" /></a></div>
<br />
Click <b>OK</b>. Then run <b>autorun.sh</b> in the window that will appear (this is the content of the virtual CD).<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpwucTUszCQp3YEytjyk383C0h06HDnG2itQCIP2wZLnKklDTLX7tthYH9YKZ7AaymVGILDyQYHF97qmRzM0dHNP0Y2MuQhAS8bV3ObfK5svz7PsGW7uPG1PMTAszvNQO9VTxEA_6trCs/s1600/vm-28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpwucTUszCQp3YEytjyk383C0h06HDnG2itQCIP2wZLnKklDTLX7tthYH9YKZ7AaymVGILDyQYHF97qmRzM0dHNP0Y2MuQhAS8bV3ObfK5svz7PsGW7uPG1PMTAszvNQO9VTxEA_6trCs/s640/vm-28.png" width="640" /></a></div>
<br />
Then choose Execute.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3_DI306jnGPZBgflAy6m6BXQyjARZZj4dnQ7xccOPwI8FEQmKxXVeCxisUvH2NjqI4bTxWmS8DFITCz9XzaC1zbyp2oeQWo_-HUZOLy249JYJDChSgLSToXPw2mqwCq5u7OfdmiPrAw0/s1600/vm-29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3_DI306jnGPZBgflAy6m6BXQyjARZZj4dnQ7xccOPwI8FEQmKxXVeCxisUvH2NjqI4bTxWmS8DFITCz9XzaC1zbyp2oeQWo_-HUZOLy249JYJDChSgLSToXPw2mqwCq5u7OfdmiPrAw0/s640/vm-29.png" width="640" /></a></div>
<br />
Authorize the script to have additional privileges by entering your password and pressing <b>OK</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFOG65EaEMtOaCkjMgGgTloGEDsydRoCDOVnKSOxIa_vBHHvWGbFFkYQ3ZPezf6xq19YMoXY6aJH-9APykHJ6sR46hvWcrC-FQjkl8R-XiMccqtPOfvAE_B_Krqb7y7XXz6aSiOFR1IEE/s1600/vm-30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFOG65EaEMtOaCkjMgGgTloGEDsydRoCDOVnKSOxIa_vBHHvWGbFFkYQ3ZPezf6xq19YMoXY6aJH-9APykHJ6sR46hvWcrC-FQjkl8R-XiMccqtPOfvAE_B_Krqb7y7XXz6aSiOFR1IEE/s640/vm-30.png" width="640" /></a></div>
<br />
The script will run for a few seconds, installing some packages. Once finished you will have to press <b>Enter</b> to exit.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTqv3wwf0u4hz7_OADqrkAWXA-v7UYq6R25OhtIZjDujvroE5TNfu3-tQyznW_Q_RW_KW1_CUiotzb7eJ7I9EP2Nh_UpTxTSXluH_I9LI687T7rdBickfN_fl2f48Cq7qYp061tR9sYI8/s1600/vm-31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTqv3wwf0u4hz7_OADqrkAWXA-v7UYq6R25OhtIZjDujvroE5TNfu3-tQyznW_Q_RW_KW1_CUiotzb7eJ7I9EP2Nh_UpTxTSXluH_I9LI687T7rdBickfN_fl2f48Cq7qYp061tR9sYI8/s640/vm-31.png" width="640" /></a></div>
<br />
<i>Extra</i>: In case of Lubuntu, you need to install another package. Open the terminal by pressing <b>Ctrl + Alt + T</b> or clicking the start menu -> <b>System Tools</b> -> <b>XTerm</b>. Then type (you cannot copy at this point):<br />
<pre class="brush:bash">sudo apt-get install --yes virtualbox-guest-dkms</pre>
Then enter your password when it asks for it (it will download about 60MB).<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCFsNnq4TR_XQBEJtSJNg-L8r4a2qnFOl76qNh1LfKVS2qwDcFlVJUwvnXSnsHY5I9m-4_O7z0a4SK05vzwTpOGd2vEP9Nxq748FG_R7L_nqgarehzrhkeVxsJGl_bUzlCr1Q-CS-n9VE/s1600/vm-33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCFsNnq4TR_XQBEJtSJNg-L8r4a2qnFOl76qNh1LfKVS2qwDcFlVJUwvnXSnsHY5I9m-4_O7z0a4SK05vzwTpOGd2vEP9Nxq748FG_R7L_nqgarehzrhkeVxsJGl_bUzlCr1Q-CS-n9VE/s640/vm-33.png" width="640" /></a></div>
<br />
<br />
Now shutdown the machine, go to this VM <b>Settings</b> -> <b>General</b> -> <b>Advanced</b> tab, and set <b>Shared Clipboard</b> to <b>Bidirectional</b>. This way you can copy instructions from your host OS to the VM terminal, and copy any error message you face from the VM terminal to -for example- your browser in the host OS.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglMfB-XQ9_qs_i1_K6izKx4CwOFG2h-k-xe26OdC9_u3Fg30zkLtD9vSh3jFCBSdV0qbseOVD2TAY9pQrsV1z04aY-MDNRzVfNvmyBHZSyQB7NOnRSa_neisUVN5D2VyrNOt7qWj1fWV4/s1600/vm-32.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglMfB-XQ9_qs_i1_K6izKx4CwOFG2h-k-xe26OdC9_u3Fg30zkLtD9vSh3jFCBSdV0qbseOVD2TAY9pQrsV1z04aY-MDNRzVfNvmyBHZSyQB7NOnRSa_neisUVN5D2VyrNOt7qWj1fWV4/s640/vm-32.png" width="640" /></a></div>
<br />
Now let's start the VM again.<br />
<br />
This time, try to copy any text (for example, a url) from your host OS. Then search for Firefox browser in the VM and paste it. It works!<br />
<br />
One more thing has happened. If you resize the VM window, it will scale and keeps the aspect ratio. If you find the screen is getting '<i>compressed</i>' and losing the correct ratio, press <b>Right Ctrl + C</b>.<br />
<br />
Now we have a clean and helpful environment to work with.<br />
Let's start with the development installations. Open the terminal and get ready!<br />
<br />
<span style="font-size: x-large;">Install Ruby</span><br />
<br />
(references used: <a href="https://www.digitalocean.com/community/tutorials/how-to-install-ruby-on-rails-with-rbenv-on-ubuntu-14-04" target="_blank">DigitalOcean</a>, <a href="https://gorails.com/setup/ubuntu/14.04" target="_blank">GoRails</a>)<br />
<br />
First there are some packages to install before Ruby.<br />
<br />
Update out package list <br />
<pre class="brush:bash">sudo apt-get update</pre>
<br />
Install git, curl, and some other packages for compilation (~90MB)<br />
<pre class="brush:bash">sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev</pre>
<br />
Now install rbenv (the tool for manging Ruby versions, better than installing Ruby directly)<br />
<pre class="brush:bash">cd
git clone git://github.com/sstephenson/rbenv.git .rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc</pre>
<br />
Next, install Ruby and set the installed version to be the default. (this will take a while to download and compile)<br />
<pre class="brush:bash">rbenv install -v 2.2.3
rbenv global 2.2.3</pre>
<br />
Now if you type<br />
<pre class="brush:bash">ruby -v</pre>
you will see the default ruby version.<br />
<br />
Let's add one more step, disable documentation installation. We do not need them.<br />
<pre class="brush:bash">echo "gem: --no-document" > ~/.gemrc</pre>
<br />
<span style="font-size: x-large;">Install Bundler Gem</span><br />
<br />
This is the first gem to install. It is responsible for handling later gems in Rails projects.<br />
<pre class="brush:bash">gem install bundler</pre>
<br />
<span style="font-size: x-large;">Install Rails </span>
<br />
<pre class="brush:bash">gem install rails -v 4.2.5</pre>
<br />
When it finishes, if you type<br />
<pre class="brush:bash">rails -v</pre>
you will see the default rails version.<br />
<br />
<span style="font-size: x-large;">Install Node.js</span><br />
<br />
(reference: <a href="https://github.com/nodesource/distributions">https://github.com/nodesource/distributions</a>)<br />
Node.js is a platform for development using Javascript. Rails uses Node.js in a certain step when joining multiple Javascript files together for better performance. You may not need this at first, but let's setup the environment the right way for future use and future reference.<br />
<br /></div>
<pre class="brush:bash">curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
sudo apt-get install -y nodejs</pre>
<br />
Now type<br />
<pre class="brush:bash">node -v</pre>
to check the installed version.<br />
<br />
<span style="font-size: x-large;">Install PostgreSQL</span><br />
<br />
PostgreSQL is the biggest competitor to MySQL database. It is getting more popular, so it is nice to stay up-to-date and give it a try. But don't be scared. at first, it will not matter if you use MySQL or PostgreSQL because most of your interactions will be through Rails. (You can skip it and install MySQL)<br />
<pre class="brush:bash">sudo sh -c "echo 'deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main' > /etc/apt/sources.list.d/pgdg.list"
wget --quiet -O - http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install -y postgresql-common
sudo apt-get install -y postgresql-9.4 libpq-dev</pre>
<br />
<span style="font-size: x-large;">Create PostgreSQL User</span><br />
<br />
The last step is to create a new db user for later.<br />
<pre class="brush:bash">sudo -u postgres createuser cse -s</pre>
If you would like to set a password for the user, you can do the following<br />
<pre class="brush:bash">sudo -u postgres psql
postgres=# \password cse </pre>
then enter the password you like.<br />
<br />
<span style="font-size: x-large;">Install MySQL</span><br />
<br />
Use this command to install MySQL server and client.<br />
<pre class="brush:bash">sudo apt-get install mysql-server mysql-client libmysqlclient-dev</pre>
When prompted, you can choose to enter a password for the root user of MySQL or leave it blank. Whatever you choose, then use the down arrow to go to <b>OK</b> line and hit enter.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXuguywDd1ntbtkV2mbsCv5_CTvB4rKqRxxwyB5lYxUm6qr3g74wwE3Q5ZcD-NLMIREyFNrCgrZZTlICCd_h8NexmMBD5O88zpKKLql5FBI80iv_hA4O3x3u2YSOC_4f1zmiRMZhOCnsg/s1600/mysql-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="426" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXuguywDd1ntbtkV2mbsCv5_CTvB4rKqRxxwyB5lYxUm6qr3g74wwE3Q5ZcD-NLMIREyFNrCgrZZTlICCd_h8NexmMBD5O88zpKKLql5FBI80iv_hA4O3x3u2YSOC_4f1zmiRMZhOCnsg/s640/mysql-1.png" width="640" /></a></div>
<br />
<br />
The root user has access to all databases. It is not safe to use it in production environment. It is better to create a new user for each project and give access to project's database only. But since this is a development environment, we can use root user.<br />
<br />
<span style="font-size: x-large;">Install Atom Editor</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPBwrDMIhx0j1nXiNsR1Je-HtfvRkPlpv37M_FowK8D-I8y5jMTM9Wj2EGGqHfd2rn1SqRRTdwQInHH7xEGy8nUVoyd5nZaRssQSJh-qpTjNrcCKJFpTSx5uywCYmkSVglGCH8HisVxBE/s1600/screenshot-main-f609d95c29e5190787970f8c83762fcb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPBwrDMIhx0j1nXiNsR1Je-HtfvRkPlpv37M_FowK8D-I8y5jMTM9Wj2EGGqHfd2rn1SqRRTdwQInHH7xEGy8nUVoyd5nZaRssQSJh-qpTjNrcCKJFpTSx5uywCYmkSVglGCH8HisVxBE/s640/screenshot-main-f609d95c29e5190787970f8c83762fcb.png" width="640" /></a></div>
<br />
Now we need a nice editor to help us with developmen. Recently, I switched from <a href="http://www.sublimetext.com/" target="_blank">Sublime Text</a> to <a href="https://atom.io/" target="_blank">Atom Editor</a>. So I will continue with using Atom. Feel free to use whatever you want.<br />
You can install Atom by downloading the installer (*.deb) from their home page, or install it from the terminal. To avoid explaining how to share folders between VM and host, I will install it from the terminal. You can follow my steps or download the installer from the VM browser.<br />
<pre class="brush:bash">sudo add-apt-repository ppa:webupd8team/atom
sudo apt-get update
sudo apt-get install atom</pre>
<br />
<span style="font-size: x-large;">Install Gitg</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://screenshots.ubuntu.com/screenshots/g/gitg/2384_large.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://screenshots.ubuntu.com/screenshots/g/gitg/2384_large.png" height="480" width="640" /></a></div>
<br />
The last optional step is to install Gitg. It is a graphical interface to use Git. It make things a bit easier when it comes to committing changes, reviewing history, and other operations you may need.<br />
<br />
<pre class="brush:bash">sudo apt-get install gitg</pre>
<br />
<br />
That's all folks! Your machine is ready. You can compress the machine and share it with others or keep it as a backup as a ready-to-use machine.</div>
Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-308361232795615929.post-14188993225785313212015-11-30T00:19:00.000+02:002015-11-30T10:56:42.171+02:00CSE 321 Project - Part 1 - Install Lubuntu on VirtualBox<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-size: small;"><i>This is one post in a series of posts related to CSE 321 - Software Engineering project for ASU CSE 2017 students. The series starts from setting up the environment, to planning the work on the website project, to implementation </i></span><br />
<br />
In this tutorial, we will start a virtual machine and install Lubuntu or Ubuntu.<br />
<span style="font-size: x-large;"><br /></span>
<span style="font-size: x-large;">Install VirtualBox</span><br />
<br />
The first thing is downloading and installing VirtualBox:<br />
<a href="https://www.virtualbox.org/wiki/Downloads">https://www.virtualbox.org/wiki/Downloads</a><br />
<br />
And assuming that you are working on Linux, then you'll go for the first link. The download link is for both 32-bit and 64-bit computers.<br />
(<i>note: x86 means 32-bit, amd64 means 64-bit</i>)<br />
<br />
<span style="font-size: x-large;">Create a New Lubuntu Machine</span><br />
<br />
Let's create a new machine by clicking the <b>New</b> icon.<br />
<br />
Enter a suitable name for your machine. Any name to remind you what this machine does.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNuWPIv6hPi6v61GWAppJYoIU4w7oJukEh6RguTaYl-NKwdvpLalPqMMY5mveD-CGV8jg__e2TDdn-x_TG7pMfvb117mKZKAMEgmRMk993uUfyknL9JjjxCBRzDTkodeY6hNfeSM94wrU/s1600/vm-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNuWPIv6hPi6v61GWAppJYoIU4w7oJukEh6RguTaYl-NKwdvpLalPqMMY5mveD-CGV8jg__e2TDdn-x_TG7pMfvb117mKZKAMEgmRMk993uUfyknL9JjjxCBRzDTkodeY6hNfeSM94wrU/s1600/vm-1.png" /></a></div>
<br />
Select the amount of memory this machine is allowed to use. If you have plenty of RAM, give it 2GB. No worries, this value can be changed later.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV9x8t-yxW2W3pQrUdKCENbYfydl8Sji5ScAbQLlA7hHW8sj7T0WyXpj9iptwa0xXLx8SXELFSqifUoI6zjNO0gpS2xM4fG9BDUsiX8706WV_VB170KmXabDbhJS74aEXCYguuOg2EJfA/s1600/vm-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV9x8t-yxW2W3pQrUdKCENbYfydl8Sji5ScAbQLlA7hHW8sj7T0WyXpj9iptwa0xXLx8SXELFSqifUoI6zjNO0gpS2xM4fG9BDUsiX8706WV_VB170KmXabDbhJS74aEXCYguuOg2EJfA/s1600/vm-2.png" /></a></div>
<br />
Leave the default option to create a new virtual hard disk.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfd7PkCGhpqeuAzksx3c7hvGgyZtJgriema6WZTa45cp6jJrk04GDQUJlcBomRpDXhJaCy1FUa2s9rPuC9PWj2HkUPKp0JS4ptSfOBF9xkyi6BwxBjvmg-MV4bHJPE8eMnWa1jWLqa-zw/s1600/vm-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfd7PkCGhpqeuAzksx3c7hvGgyZtJgriema6WZTa45cp6jJrk04GDQUJlcBomRpDXhJaCy1FUa2s9rPuC9PWj2HkUPKp0JS4ptSfOBF9xkyi6BwxBjvmg-MV4bHJPE8eMnWa1jWLqa-zw/s1600/vm-3.png" /></a></div>
<br />
Leave the VDI option as it is.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj1BxIidmQQgtObzsZWN8rDlJTRcpl_5EbxbM3sCCdgsXHqmmQvSXa-R4XDJv2wuhwPUw30LqSyVlNuwraeYLG0S3Kl_ahPHqJr3_c5xIvcqgOpL1xFnIOs_TT5ZqLiVjw0c0wtH25EsI/s1600/vm-4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="455" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj1BxIidmQQgtObzsZWN8rDlJTRcpl_5EbxbM3sCCdgsXHqmmQvSXa-R4XDJv2wuhwPUw30LqSyVlNuwraeYLG0S3Kl_ahPHqJr3_c5xIvcqgOpL1xFnIOs_TT5ZqLiVjw0c0wtH25EsI/s640/vm-4.png" width="640" /></a></div>
<br />
Leave the dynamic size as it is. This option allows the virtual hard disk (the file created) to start small then expand later to a max limit. The other option will create the full sized file from the beginning.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZlQIULpPoatsR9Gcyzu2WV77KwRgRqhX4EMYePklEacMiMbxY7nlPlg8BBme-O-Qw970Z0D43No0LU-HAFEO99fGwDRLuf6-oWgOPTp2_tlMNm2W-xpUrwQUQJ-HLiFdW480dfKYv04w/s1600/vm-5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="455" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZlQIULpPoatsR9Gcyzu2WV77KwRgRqhX4EMYePklEacMiMbxY7nlPlg8BBme-O-Qw970Z0D43No0LU-HAFEO99fGwDRLuf6-oWgOPTp2_tlMNm2W-xpUrwQUQJ-HLiFdW480dfKYv04w/s640/vm-5.png" width="640" /></a></div>
<br />
Choose a different name for the virtual hard disk or leave it with the same name as the virtual machine name. And you can leave the size at it is for now. Actually 8GB is more than enough.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6XSn2IYZwvssCMFnBIjVwDKxo6oowZI4bEvVXwZOO_38mNkHALfhphS2NoTriLFRIKb9tHDy81ltcTDrVX13DzaEi7R4BJv8CGWCQLjnrU8BqGHkQZ8iIgPnH1FDAeIfNhnGFGcuZX3c/s1600/vm-6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="455" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6XSn2IYZwvssCMFnBIjVwDKxo6oowZI4bEvVXwZOO_38mNkHALfhphS2NoTriLFRIKb9tHDy81ltcTDrVX13DzaEi7R4BJv8CGWCQLjnrU8BqGHkQZ8iIgPnH1FDAeIfNhnGFGcuZX3c/s640/vm-6.png" width="640" /></a></div>
<br />
Now the VM is ready.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt9Qiwnhw9s6ilKI61DeJeyHXmASoK3M97qkkdNL-ZsNGX10fd5jASeXZQ02DoEADIJhf9sVlOBR9p2_5k2wK-LlneALJINH82xv2E3oQrVU2FNISiIL3OoJgEVe3wZhpdiFgOczhV1nY/s1600/vm-7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt9Qiwnhw9s6ilKI61DeJeyHXmASoK3M97qkkdNL-ZsNGX10fd5jASeXZQ02DoEADIJhf9sVlOBR9p2_5k2wK-LlneALJINH82xv2E3oQrVU2FNISiIL3OoJgEVe3wZhpdiFgOczhV1nY/s640/vm-7.png" width="640" /></a></div>
<br />
<span style="font-size: x-large;">Install Lubuntu</span><br />
<br />
Before starting, let's mount the Lubuntu iso image as a CD to use it for installation.<br />
<br />
Select the machine and click <b>Settings</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9zhQYxJ8oxmQSerdvGcOkwszVLt9Mcjhyphenhyphen4qnxm-D8UnYzd7-9kIr7AblWS8nY3igcejkpO36amm62xuy34RKyQXx66v8xryNETsg5kgCTj1NyKNl-_bxrSlW3MFW9W_Bu68SBj-XCQQw/s1600/vm-8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9zhQYxJ8oxmQSerdvGcOkwszVLt9Mcjhyphenhyphen4qnxm-D8UnYzd7-9kIr7AblWS8nY3igcejkpO36amm62xuy34RKyQXx66v8xryNETsg5kgCTj1NyKNl-_bxrSlW3MFW9W_Bu68SBj-XCQQw/s640/vm-8.png" width="640" /></a></div>
<br />
<br />
Go to Storage, and select the empty CD.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq7fyjmC2R0y_28sLEuORHG8l5hetbNYwZCRJ5hE5B_DqoYn2FbLHmo6-8SpI7qt0_v8ubKVoBEReicdSsNYmsru9Zj55P__9Lhkuh_4T4tYjzs99zdF2p2PMkBAhPxOX2DA3nnJ-VX8A/s1600/vm-9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq7fyjmC2R0y_28sLEuORHG8l5hetbNYwZCRJ5hE5B_DqoYn2FbLHmo6-8SpI7qt0_v8ubKVoBEReicdSsNYmsru9Zj55P__9Lhkuh_4T4tYjzs99zdF2p2PMkBAhPxOX2DA3nnJ-VX8A/s640/vm-9.png" width="640" /></a></div>
<br />
Click on the small CD icon on the far right. And choose <b>Choose Virtual Optical Disk file</b>.<br />
Then browse your folders and select the iso file you downloaded (<a href="http://releases.ubuntu.com/14.04/ubuntu-14.04.3-desktop-amd64.iso" target="_blank">Ubuntu iso</a> , <a href="http://cdimage.ubuntu.com/lubuntu/releases/14.04/release/lubuntu-14.04.3-desktop-amd64.iso" target="_blank">Lubuntu iso</a>) then click <b>Open</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7jhe7oVL2pOE_2WQgvhZATNAE2D1KgDmhtdLJDfzcSQm_sKSSy-20O26XJnY_Z6IRTLCm_9sYo5oqc8pHGP2PMoVDWqozcV4HrfKX2e63vrjpcCGVtAmClRo7_beVVl6uAKbbc_TRjvo/s1600/vm-10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="492" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7jhe7oVL2pOE_2WQgvhZATNAE2D1KgDmhtdLJDfzcSQm_sKSSy-20O26XJnY_Z6IRTLCm_9sYo5oqc8pHGP2PMoVDWqozcV4HrfKX2e63vrjpcCGVtAmClRo7_beVVl6uAKbbc_TRjvo/s640/vm-10.png" width="640" /></a></div>
<br />
Click <b>OK</b>. When done.<br />
<br />
Now we are ready to start the machine. Select the machine and click <b>Start</b> or double-click it.<br />
<br />
Choose <b>English</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGPzw533N_sBIga2t-klgF7ADZIBhYzWSFit5_QDoH2q5T3YUMSWPIFyug1crse3ty1exfN9XzWdEHX0ZAZGDK7eexTDAXzJDv1ZutFs_vpfZ_vHdVbKygkJzoUwdzC84cKdAviV-Zs4U/s1600/vm-12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGPzw533N_sBIga2t-klgF7ADZIBhYzWSFit5_QDoH2q5T3YUMSWPIFyug1crse3ty1exfN9XzWdEHX0ZAZGDK7eexTDAXzJDv1ZutFs_vpfZ_vHdVbKygkJzoUwdzC84cKdAviV-Zs4U/s1600/vm-12.png" /></a></div>
<br />
<br />
Choose <b>Install Lubuntu</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiELGK905w0M9-OcUjzfH7yI3smKrE2SZLYZ3fiFlJTBmm8zuRFdfoJHAe8DOox9n_j_XbQzCARKRGOEcIciBuFlTa17mfrITd1dyaX0IvRtIGxx-ACRhN1GrIgSN2T2D2bA_AGUEcGePo/s1600/vm-13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiELGK905w0M9-OcUjzfH7yI3smKrE2SZLYZ3fiFlJTBmm8zuRFdfoJHAe8DOox9n_j_XbQzCARKRGOEcIciBuFlTa17mfrITd1dyaX0IvRtIGxx-ACRhN1GrIgSN2T2D2bA_AGUEcGePo/s1600/vm-13.png" /></a></div>
<br />
Wait for a few seconds until it boots the installer.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT8gfeikhJkib1cOjyYTS5RWuahNNnX7FqrExJ3-XpJ-tRP9whA-2cenVQbrKrHyWT4fTfknKiWXKgr2rHJ-YoLFGoKpU6QdTvFDxlzg87PFLvKZx30ExFxd3x4jtWxsp8SymzNDUR85M/s1600/vm-14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="355" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT8gfeikhJkib1cOjyYTS5RWuahNNnX7FqrExJ3-XpJ-tRP9whA-2cenVQbrKrHyWT4fTfknKiWXKgr2rHJ-YoLFGoKpU6QdTvFDxlzg87PFLvKZx30ExFxd3x4jtWxsp8SymzNDUR85M/s640/vm-14.png" width="640" /></a></div>
<br />
Choose <b>English</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEz9Xli0qZT4MzgKC8bdy2kiolTOuLRdEy-28qJ5edShn2NmDZ1fM4tpWsOiBm59Zx9A85makaXbxWdFqFSj71n6PudOCTl8nuXFdYaV_0VICZhfD0A4b3IOl65w5ckLc5aPJ9ux5vwWA/s1600/vm-15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEz9Xli0qZT4MzgKC8bdy2kiolTOuLRdEy-28qJ5edShn2NmDZ1fM4tpWsOiBm59Zx9A85makaXbxWdFqFSj71n6PudOCTl8nuXFdYaV_0VICZhfD0A4b3IOl65w5ckLc5aPJ9ux5vwWA/s640/vm-15.png" width="640" /></a></div>
<br />
Click <b>Continue</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlyHxlgMFXzgRDD9ST5sacSzKbSBAFybC3w6vXZSoD7AjPrALU-htO4eNYFu92hUDIgwMCO7akpeSsCO2_CWdqQhXtPfXpn8MmIauHwis1Iauo0wQkJrsE3r8_AsGRoZq2jx37sPE4OJY/s1600/vm-16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlyHxlgMFXzgRDD9ST5sacSzKbSBAFybC3w6vXZSoD7AjPrALU-htO4eNYFu92hUDIgwMCO7akpeSsCO2_CWdqQhXtPfXpn8MmIauHwis1Iauo0wQkJrsE3r8_AsGRoZq2jx37sPE4OJY/s640/vm-16.png" width="640" /></a></div>
<br />
Now for the critical part of formatting if it was a real installation. Leave the first option as it is. After all, this is a virtual disk created for Lubuntu. Not your actual disk. Click <b>Install Now</b>. Then click <b>Continue</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBYaQoH8ngsrpz6s4H8MJjyt4yWsNulegxGDu6AP1e8D98C56GR_fFRuE_qvOE2jWsvquvl9HQ771UuDxcvWd1NWa7qUObD0ktaR54CJLeOA7oRccK-uvcrlFrOt_nB279nDGZB1j9d00/s1600/vm-17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBYaQoH8ngsrpz6s4H8MJjyt4yWsNulegxGDu6AP1e8D98C56GR_fFRuE_qvOE2jWsvquvl9HQ771UuDxcvWd1NWa7qUObD0ktaR54CJLeOA7oRccK-uvcrlFrOt_nB279nDGZB1j9d00/s640/vm-17.png" width="640" /></a></div>
<br />
Choose the city of Cairo or any other city you prefer, and click <b>Continue</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpbHH0C8lV9XbW2s_P6yp1JH-YerbtJJ1CUEzESwNDPpmzLJSDrvwrpLbk3-lefhj7LWdWWbEPz2Nc2O_y_O6Ns8WFEcGXk_6O4k_1bhJdUflGAd6iMEKslljEsDfd2mnAeGATX3Xl3Bg/s1600/vm-18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpbHH0C8lV9XbW2s_P6yp1JH-YerbtJJ1CUEzESwNDPpmzLJSDrvwrpLbk3-lefhj7LWdWWbEPz2Nc2O_y_O6Ns8WFEcGXk_6O4k_1bhJdUflGAd6iMEKslljEsDfd2mnAeGATX3Xl3Bg/s640/vm-18.png" width="640" /></a></div>
<br />
Leave the keyboard layout as it is and press <b>Continue</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7eiK7C3dDy-4A-TVTaN9kPXE5r3Zzn4XjEIiqiYWVRxYrFaUjJLEHb0aFXb2hzVqBANF_E6N79HxzCionbHHbeK_onsgqd8pUmdFKGX_D5koGXiqyQPGCRcu16MI4RUiLzEr47LlOjmU/s1600/vm-19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7eiK7C3dDy-4A-TVTaN9kPXE5r3Zzn4XjEIiqiYWVRxYrFaUjJLEHb0aFXb2hzVqBANF_E6N79HxzCionbHHbeK_onsgqd8pUmdFKGX_D5koGXiqyQPGCRcu16MI4RUiLzEr47LlOjmU/s640/vm-19.png" width="640" /></a></div>
<br />
Enter your name and choose the username and password. Click <b>Continue</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja6yDBaa82EjrYX-FMERGbR4J3J1ZLdTaMNZBwdz2YbduNq_Mr7Ou7ax29hppEYOCtZUraq03ZOmS3NoYYu8bfGY5FIkNuPYh7NCkKbO2fckirbEyqR3nYVnmwa_OAjQDLSgz9LFYeKZc/s1600/vm-20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja6yDBaa82EjrYX-FMERGbR4J3J1ZLdTaMNZBwdz2YbduNq_Mr7Ou7ax29hppEYOCtZUraq03ZOmS3NoYYu8bfGY5FIkNuPYh7NCkKbO2fckirbEyqR3nYVnmwa_OAjQDLSgz9LFYeKZc/s640/vm-20.png" width="640" /></a></div>
<br />
Now leave it for a few minutes to install and pull only some basic data from the Internet.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-uySd70kNsGcKB6aqVu4Id6RrIeqLvtVQz_aBE1E8G2767mil2sX-SoWGnHqqoG2bYXkULBcyYtgFhrUHaLzYo10tW3TBRoG7T3bYSZacGV6K71OoYeYzW8r95wdjkZP0xUKg491WvVQ/s1600/vm-21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-uySd70kNsGcKB6aqVu4Id6RrIeqLvtVQz_aBE1E8G2767mil2sX-SoWGnHqqoG2bYXkULBcyYtgFhrUHaLzYo10tW3TBRoG7T3bYSZacGV6K71OoYeYzW8r95wdjkZP0xUKg491WvVQ/s640/vm-21.png" width="640" /></a></div>
<br />
When finished, press <b>Restart Now</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix_-RaSQOlTEo4aC7bdIgwo8rjjsclCZBDmu61vM7tFcX4EMaSA-A8EekI8o208yxj2ORjeXpHWIZDopC4b1iuEbYlH0MRDXg2JMRF0YOcYJMjnWOG3UNwn91X6gRkRwziHoAK9QnBAdo/s1600/vm-22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix_-RaSQOlTEo4aC7bdIgwo8rjjsclCZBDmu61vM7tFcX4EMaSA-A8EekI8o208yxj2ORjeXpHWIZDopC4b1iuEbYlH0MRDXg2JMRF0YOcYJMjnWOG3UNwn91X6gRkRwziHoAK9QnBAdo/s640/vm-22.png" width="640" /></a></div>
<br />
The machine will eject the virtual CD it used for installation and asks you to press Enter. Do it.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLLfHoJ9YWXPn1ntRxOBniPhljkjriBjtx6SC-FlR5kahQrdtdlQ1mjrtNr0Ekaa2HzTN8wMES8u8VCLASJkzfw4tCbMkAO0dkK1KoVBl2CxaOD15bqFo9TOBG1xN3RcVmlE6xWQRCfyw/s1600/vm-23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="353" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLLfHoJ9YWXPn1ntRxOBniPhljkjriBjtx6SC-FlR5kahQrdtdlQ1mjrtNr0Ekaa2HzTN8wMES8u8VCLASJkzfw4tCbMkAO0dkK1KoVBl2CxaOD15bqFo9TOBG1xN3RcVmlE6xWQRCfyw/s640/vm-23.png" width="640" /></a></div>
<br />
The fresh Lubuntu OS will boot.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih-ZtIgQ05dtLFJkHb9Iz0fw1XFVrnCrzToy3l4DCl-Bh50hoeisUrayVv5m_00ZxmkMdJSOTIHUHPVXGSZaIrMjQGEd2zVoa1y39pSbzvaV8OC1LWyghgtu_yVjxx0QEairHKK6mY00Y/s1600/vm-24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih-ZtIgQ05dtLFJkHb9Iz0fw1XFVrnCrzToy3l4DCl-Bh50hoeisUrayVv5m_00ZxmkMdJSOTIHUHPVXGSZaIrMjQGEd2zVoa1y39pSbzvaV8OC1LWyghgtu_yVjxx0QEairHKK6mY00Y/s640/vm-24.png" width="640" /></a></div>
<br />
The login dialog will appear. Enter your password.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJo83L8sCCLdYtTGgZC8pdFwQ6xEqRZ0yC4GwMoRDdT-WhjoxhBTAV5Sp5EtDZIing_O0qOugeLNqBBKgv7yEnB1TZyGC6Wld__bPV4yy3DHwgCDvkVAVZcLX3IIpFx4ONbrGFZtubbbM/s1600/vm-25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJo83L8sCCLdYtTGgZC8pdFwQ6xEqRZ0yC4GwMoRDdT-WhjoxhBTAV5Sp5EtDZIing_O0qOugeLNqBBKgv7yEnB1TZyGC6Wld__bPV4yy3DHwgCDvkVAVZcLX3IIpFx4ONbrGFZtubbbM/s640/vm-25.png" width="640" /></a></div>
<br />
Welcome to your new machine. Now you can shut it down and start it again whenever you want.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7bGihQCujOclyOtGdmXZD-XV1msnmfVIFX-sg_kp47U38kFhnh9oMjcND3-vl7__Z1jq3B3wAbL26AyVXBQn-R2MF9WwzhchI0E0inCB7QjSsBsG5i6L3ZbijuUfLhhwb_9graZxdXww/s1600/vm-26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7bGihQCujOclyOtGdmXZD-XV1msnmfVIFX-sg_kp47U38kFhnh9oMjcND3-vl7__Z1jq3B3wAbL26AyVXBQn-R2MF9WwzhchI0E0inCB7QjSsBsG5i6L3ZbijuUfLhhwb_9graZxdXww/s640/vm-26.png" width="640" /></a></div>
<br />
That's all folk! :) <br />
<br />
In the next tutorial, we will work on installing:<br />
<ul style="text-align: left;">
<li>Ruby</li>
<li>Rails</li>
<li>Node.js</li>
<li>PostgresSQL</li>
<li>Atom Editor</li>
<li>Git</li>
<li>Gitg (a graphical tool to use with Git) </li>
<li>Enable copy and paste between the VM and the computer, and enable flexible display resolution.</li>
</ul>
Take your time and try and fail without worries. Virtual machines are created and deleted all the time. If you mess it up, delete it and try again.<br />
<br />
[<i>update</i>: <a href="http://3adly.blogspot.com.eg/2015/11/cse-321-project-part-2-install-ruby.html" target="_blank">Part 2</a> is available now] </div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-45983035260891983502015-10-16T20:50:00.003+02:002015-10-16T20:52:00.502+02:00Custom 404 Page in Slim Framework 3<div dir="ltr" style="text-align: left;" trbidi="on">
In the past month, I've been learning Slim 3 PHP framework through a
website as a real experiment of how powerful Slim Framework is. In this
post, I'm sharing a code snippet for rendering 404 page.<br />
<br />
Between creating a Slim Container and creating the Slim App, I override the default <i>notFoundHandler</i>.<br />
<pre class="brush:php">$container = new Slim\Container;
...
// Override the default Slim Not Found Handler
$container['notFoundHandler'] = function ($c) {
return function ($request, $response) use ($c) {
return $c['view']->render($response, '404.html', [])->withStatus(404);
};
};
...
// Initialize the app
$app = new Slim\App($container);
</pre>
<br />
In my case, I use Twig template engine for views. If your case is different, change the render line.</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-75296930003507337292015-10-16T20:16:00.002+02:002016-12-22T16:41:19.023+02:00File System Caching in Slim Framework 3<div dir="ltr" style="text-align: left;" trbidi="on">
In the past month, I've been learning Slim 3 PHP framework through a website as a real experiment of how powerful Slim Framework is. In this post, I'm sharing a code snippet of how I implemented the Filesystem caching.<br />
<br />
The summary is:<br />
- Add a middleware for caching successful responses (200 OK) by saving the response in a file. To avoid long file names or deep paths, I hash the response URL.<br />
- When receiving a request, check its hashed path in the cached files. If found, return the content. Else, continue with initializing the app, routes, and everything else. This way, there is no initialization overhead for cached requests.<br />
<br />
Here is the beginning of my index.php:<br />
<pre class="brush:php">require './lib/initializer.php';
function getCachePath($uri) {
$webpage_handle = md5($uri);
return ROOT."/public/cache/".$webpage_handle;
}
/* Check for cached file here before loading anything or opening DB */
if(CACHING_ENABLED) {
$requestedURI = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$cachedFilePath = getCachePath($requestedURI);
if (file_exists($cachedFilePath) && (filemtime($cachedFilePath) > (time() - CACHE_SECONDS ))) {
$cacheFile = file_get_contents($cachedFilePath);
die($cacheFile);
}
}
$cacheMiddleware = function ($request, $response, $next) {
/* process the request */
$response = $next($request, $response);
/* cache response */
if(CACHING_ENABLED && ($response->getStatusCode()==200) && ($request->getMethod() == 'GET')) {
$cachedFilePath = getCachePath($request->getUri());
$file = fopen($cachedFilePath,"w");
fwrite($file, $response->getBody());
fclose($file);
}
return $response;
};
</pre>
<br />
<br />
First, I require a file where I initialize some configs and constants, like <i>CACHE_SECONDS</i>.<br />
<br />
Then, I create a function for deciding the cache path for requests. To use it in both reads and writes.<br />
<br />
Then, -if caching is enabled- I check for the existence of the requested file and the creation date. This way I override the file if it was older. (*<i>check the end of the post for another better way for expiry</i>)<br />
<br />
Next, you can see the caching middleware I use with some routes I wish to cache. Here is an example:<br />
<pre class="brush:php">$app->get('/', function ($request, $response, $args) {
// some app-related code
})->setName('home')->add($cacheMiddleware);
</pre>
<br />
And that's it. The file system is now implemented with minimal code. Of course there are many changes and upgrades depending on your case.<br />
<br />
---------------------------------- <br />
Notes:<br />
*Better than just checking for file creation date (and leaving the files to increase), you can remove the date-check part and add a cron job for removing files older than a specific duration. Here is how my cron job script looks like:<br />
<pre class="brush:bash">CACHE_DIR=$1;
cd $CACHE_DIR && find * -type f -cmin +5 -exec rm {} \;
</pre>
<br />
It takes the path to clean in the cron job command. And removes any file older than 5 minutes.<br />
<br />
The cron job looks like:<br />
<pre class="brush:bash">*/5 * * * * bash ~/path/to/cache/expiry/script.sh ~/path/to/cache/folder/
</pre>
<br />
<br />
<br /></div>
Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-308361232795615929.post-41882731998523228472015-08-29T20:23:00.000+02:002015-09-01T15:13:49.481+02:00Arabic-friendly Exeption reporting in Slim Framework 2 & 3<div dir="ltr" style="text-align: left;" trbidi="on">
Today I've been upgrading my Slim PHP project from version 2 to version to version 3 beta. And one of the issues I had was reporting my Arabic exceptions from some models I have. I did this before in version 2, to end up with moving from this scrambled message:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPbhfq4GopPZdZRbcrhuuwAJy6DMMEljIoFMegEq2-x89LBFU0OCbICnf2iiVU0bO2dLnHiTVeUI6Jz9V02tAYKIOPnhQAaGN45EDF_DfdIXN7iGXQtLGNKzo4IL9ixDRgvidvd96kmaM/s1600/slim_ex_before.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPbhfq4GopPZdZRbcrhuuwAJy6DMMEljIoFMegEq2-x89LBFU0OCbICnf2iiVU0bO2dLnHiTVeUI6Jz9V02tAYKIOPnhQAaGN45EDF_DfdIXN7iGXQtLGNKzo4IL9ixDRgvidvd96kmaM/s400/slim_ex_before.png" width="400" /></a></div>
<br />
<br />
to this one:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMsD51ONFiCtruSLAVBh4K24lEVOMO5aa-In6QE9GHYZ_4v45VQFH94L6u4UzjQFg8xw6KBtcQjJl8d_3f5MDA0i_0tv9IlgvEE07CQ2xF9CvR_O0zxa9qCUEAsRyZC3jabr5kVS1wqhc/s1600/slim_ex_after.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMsD51ONFiCtruSLAVBh4K24lEVOMO5aa-In6QE9GHYZ_4v45VQFH94L6u4UzjQFg8xw6KBtcQjJl8d_3f5MDA0i_0tv9IlgvEE07CQ2xF9CvR_O0zxa9qCUEAsRyZC3jabr5kVS1wqhc/s400/slim_ex_after.png" width="400" /></a></div>
<br />
In <b>Slim Framework 2</b>, I had to edit <b><i>Middleware / PrettyExceptions.php</i></b> to add a meta tag for content UTF-8 encoding:<br />
<pre class="brush:html"><meta http-equiv='Content-Type' content='text/html; charset=utf-8'></pre>
<br />
Now, when I moved to <b>Slim Framework 3.0.0-beta2</b>, I had to do this again but in <b><i>Handlers/Error.php</i></b>.<br />
<br />
I hope that this trick helps someone on the planet. And I think I may create a pull request for this small hack.<br />
<br />
<b>Update</b>: pull request has been merged. (<a href="https://github.com/slimphp/Slim/pull/1470" target="_blank">#1470</a>)</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-65178377225806348512015-08-29T19:59:00.002+02:002015-08-29T20:01:13.727+02:00[Node JS] Test specific files with Mocha<div dir="ltr" style="text-align: left;" trbidi="on">
This is a handy command line I use for testing specific files while developing a certain module for a faster test. I just call Mocha, (optionally) specify my preferred terminal reporter, then I call the files to go through:<br />
<br />
<pre class="brush:bash">mocha --reporter spec ./test/modules/abc.js ./test/modules/def.js</pre>
<br />
That's it. It is that simple. This way I can run tests on specific files and save some time.</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-85547618871025446102015-06-26T11:58:00.000+02:002015-06-26T12:40:00.857+02:00How to detect jQuery mmenu close event<div dir="ltr" style="text-align: left;" trbidi="on">
A few days ago, I was trying jQuery mmenu. I had a close button (called <i>#mmenu-btn</i>) that changed shape between open and close states and I wanted to make sure that it will never get stuck in a state different than the desired one.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmMmn4O-agvSxDt_uSFQxoQaFsQuOO01oNh7mSdPQJ2DpaAIWFKOxL7aOoeENjevx5cFaUBQy3WA34T416BUVYQkRcNWpP83W3HheMN60yjWXNz24yRKBAPXswt0RO2SNMeH0RIPDil9M/s320/mmenu-closed.png" width="227" /> <img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYg38dFcWsuzy5btG2UdKh-km40EXrkK8r5Y-dPrVC0_mTAzqG7QAjBgDPUzqNwyQsafDZxH075rbZv9rStmGs4WTPMvGPOGdd2P6MulCoPlQ0ucd4ky7XvJaZqX0U1we1H9tPnft_504/s320/mmenu-opened.png" width="238" /></div>
<br />
After some reading in the mmenu docs about events and configurations, and after playing a lot with the button and mmenu on desktop and mobile, I had two states to cover:<br />
1- An event is triggered by code (mmenu API): In this case, it is my code, so I can handle it.<br />
2- The user clicks/touches the part <i>outside</i> the mmenu. In this case, the mmenu does not tell me that it is closing now.<br />
<br />
For the second case, I found a nice solution that depends on how mmenu works. When mmenu opens, there is a created overlay called <i>#mm-blocker</i> that covers the part outside the menu. When this overlay is clicked, the mmenu is closed. So what I did was to bind the click event (and other similar events) of this #mm-blocker and do whatever I want to change the state of my close button.<br />
<br />
<pre class="brush:js">// if user clicks outside the mmenu, change the state of mmenu-btn
$('#mm-blocker').on('click mousedown touchstart', function() {
$('#mmenu-btn').removeClass('close');
});
</pre>
<br />
This was my little trick. I hope it helps.</div>
Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-308361232795615929.post-10248380056105588332015-06-05T13:39:00.001+02:002015-06-06T13:36:14.409+02:00Nginx + Slim Framework + phpMyAdmin on Ubuntu<div dir="ltr" style="text-align: left;" trbidi="on">
Yesterday I was giving <a href="http://slimframework.com/" target="_blank">Slim Framework</a> a test ride on my Ubuntu machine. But the machine was not ready with Nginx, or phpMyAdmin (I needed it to check the output of an <a href="http://en.wikipedia.org/wiki/Object-relational_mapping" target="_blank">ORM</a> called <a href="http://www.redbeanphp.com/" target="_blank">RedBean</a>). So I'm writing what I did exactly for my reference and for other's benefit.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg47cYMkNuDfcQpRAHtrGtKCMmKZYOsEJmAQKGKuyycoxqiqNSbYQY9aVfgTvZYpXVR6XpvpKe4POUszGIu0hSRrE3esyi2gmnHMv5KrSkTB3P6Sq5X82YTRHFVZQdf0XE75jhcLMX3LzU/s1600/slimtest.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="283" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg47cYMkNuDfcQpRAHtrGtKCMmKZYOsEJmAQKGKuyycoxqiqNSbYQY9aVfgTvZYpXVR6XpvpKe4POUszGIu0hSRrE3esyi2gmnHMv5KrSkTB3P6Sq5X82YTRHFVZQdf0XE75jhcLMX3LzU/s400/slimtest.png" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisZz1VGtd2Uht8bsaYNBS4CBctVCgdQRFg7C7WwgtZUu27g89f23-T4tewZPZDkm3vrxAxrhjjF6sDIgqgFRdN9_Qu7-KiyPnxxV-r2O4mhDiAIahuXj_HtDJR9YqCGTNU3ZgA6R4DZMk/s1600/phpmyadmin.local.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="283" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisZz1VGtd2Uht8bsaYNBS4CBctVCgdQRFg7C7WwgtZUu27g89f23-T4tewZPZDkm3vrxAxrhjjF6sDIgqgFRdN9_Qu7-KiyPnxxV-r2O4mhDiAIahuXj_HtDJR9YqCGTNU3ZgA6R4DZMk/s400/phpmyadmin.local.png" width="400" /></a></div>
<br />
<br />
First, in case you don't have any of the following packages, go ahead and do it<br />
<br />
<h3 style="text-align: left;">
Installations:</h3>
<br />
Install MySQL<br />
<pre class="brush:bash">sudo apt-get install mysql-server php5-mysql</pre>
<br />
and remember your root password you will enter in the installation process.<br />
<br />
Install Nginx<br />
<pre class="brush:bash">sudo apt-get install nginx</pre>
<br />
<br />
Install PHP-FPM (PHP FastCGI Process Manager)<br />
<pre class="brush:bash">sudo apt-get install php5-fpm</pre>
<br />
<br />
Install phpMyAdmin<br />
<pre class="brush:bash">sudo apt-get install phpmyadmin</pre>
<br />
During installation, you will be asked to choose your web server (apache2/lighttpd). But since we will use Nginx, just press the TAB key then Enter. Then you will be asked if you want to proceed with phpMyAdmin database configurations, choose yes and continue then enter your MySQL root password when asked to. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG2A89hPJjUsny0z-xdWRbsSg3TCnfSZgbxwf3BB_O_LHh7P1JTL4I1hZS2SutsTFohhljmsZlcGYKkwie6GppFTZd63ybUB5mR3L-s-u730THzihtA75cURu2FNcAcP3iLjTcHKzvqa0/s1600/phpMyAdmin-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG2A89hPJjUsny0z-xdWRbsSg3TCnfSZgbxwf3BB_O_LHh7P1JTL4I1hZS2SutsTFohhljmsZlcGYKkwie6GppFTZd63ybUB5mR3L-s-u730THzihtA75cURu2FNcAcP3iLjTcHKzvqa0/s400/phpMyAdmin-2.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRAgCRMopHU3DbSE3ohjZWpxw4j-KTntbMfjZvDOjgTQF3K6kgcdcvhy95On_0tIZ-Ri28UQdxvvxr7aTx-K6Ro1WdBhCtz2x1WGfvMUihX1emBt337ILf_ksnlQvNL5rKY1BnsohdC9U/s1600/install-phpmyadmin-ubuntu-12_04_2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRAgCRMopHU3DbSE3ohjZWpxw4j-KTntbMfjZvDOjgTQF3K6kgcdcvhy95On_0tIZ-Ri28UQdxvvxr7aTx-K6Ro1WdBhCtz2x1WGfvMUihX1emBt337ILf_ksnlQvNL5rKY1BnsohdC9U/s400/install-phpmyadmin-ubuntu-12_04_2.jpg" width="400" /></a></div>
<br />
<br />
<h3 style="text-align: left;">
Configurations:</h3>
<h4 style="text-align: left;">
FPM </h4>
<pre class="brush:bash">sudo nano /etc/php5/fpm/php.ini</pre>
<br />
or as I prefer<br />
<pre class="brush:bash">sudo gedit /etc/php5/fpm/php.ini</pre>
<br />
Find the line, <i>cgi.fix_pathinfo=1</i>. Uncomment it and change the <i>1</i> to <i>0</i>.<br />
<br />
Then<br />
<pre class="brush:bash">sudo gedit /etc/php5/fpm/pool.d/www.conf</pre>
<br />
and find the value given to listen =, to be used later in Nginx configuration.<br />
It should be <i>/var/run/php5-fpm.sock</i> or <span class="st"><span dir="ltr"><i>127.0.0.1:9000. </i></span></span> <br />
<br />
Now restart fpm<br />
<pre class="brush:bash">sudo service php5-fpm restart</pre>
<br />
<h4 style="text-align: left;">
Nginx</h4>
Now let's configure Nginx for both phpMyAdmin and Slim Framework project.<br />
<br />
<pre class="brush:bash">sudo gedit /etc/nginx/sites-enabled/default</pre>
<br />
<br />
And add the configurations for both phpMyAdmin and the Slim Framework project.<br />
<br />
<pre class="brush:plain">server {
listen 80;
server_name phpmyadmin.local;
root /usr/share/phpmyadmin;
index index.php;
location / {
try_files $uri $uri/ /index.html;
}
# pass the PHP scripts to FastCGI server listening on the php-fpm socket
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
server {
listen 80;
server_name slimtest.local;
root /home/madly/slim;
try_files $uri /index.php;
# pass the PHP scripts to FastCGI server listening on the php-fpm socket
location /index.php {
fastcgi_connect_timeout 3s; # default of 60s is just too long
fastcgi_read_timeout 10s; # default of 60s is just too long
include fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}</pre>
<br />
Now let's restart Nginx server to apply the changes<br />
<pre class="brush:bash">sudo service nginx restart</pre>
<br />
<br />
Notes on these server configs:<br />
<br />
1- The fastcgi_pass value is set to <i>unix:/var/run/php5-fpm.sock</i> because of the value I mentioned earlier in fpm configs. It may be <i>127.0.0.1:9000</i>.<br />
<br />
2- I chose to listen to port 80 but with different server names. So I must edit my hosts file<br />
<pre class="brush:bash">sudo gedit /etc/hosts</pre>
and add these two lines<br />
<pre class="brush:plain">127.0.0.1 phpmyadmin.local
127.0.0.1 slimtest.local </pre>
<br />
3- During my tests with these configs, I had the css/img files accessible in my project with paths like<br />
<pre class="brush:html"><link rel="stylesheet" type="text/css" href="/css/main.css"></pre>
by having a project structure like this<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmECwKICx0ggUA1NMa3MQGhSA58iVOETlAxbF0Xl5znBnR-POgRxic-o7mhK5c4ifbEuSb1AHgLoSjTmx3k3GejCq10n7hdRyId2BP9WkLbQ08HQuBhAGe_4k1lw6fjKs2pVsCTGnYiKM/s1600/slimproject.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmECwKICx0ggUA1NMa3MQGhSA58iVOETlAxbF0Xl5znBnR-POgRxic-o7mhK5c4ifbEuSb1AHgLoSjTmx3k3GejCq10n7hdRyId2BP9WkLbQ08HQuBhAGe_4k1lw6fjKs2pVsCTGnYiKM/s1600/slimproject.png" /></a></div>
<br />
<br />
A problem I had:<br />
The following day I had a problem with a project giving me the page "no input file specified". After inspecting the "/var/log/nginx/error.log" file, I found "(13: Permission denied)" errors. So I had to add extra permissions to the project:<br />
<pre class="brush:bash">chmod -R 755 ./</pre><br />
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-68445661059837133362015-02-23T11:25:00.002+02:002015-02-23T11:41:04.344+02:00SyntaxHighlighter Fix for Blogger Dynamic View<div dir="ltr" style="text-align: left;" trbidi="on">
After moving from old theme to the new Blogger Dynamic View theme, I hade my biggest issue: How to enable my SyntaxHighlighter again? I'd prefer find a solution that reformat ALL my blog posts throughout the years.<br />
<br />
One solution I found was adding a Javascript snippet to all my posts (<a href="http://kevin-junghans.blogspot.com/2013/01/adding-syntaxhighlighter-to-blogger.html">http://kevin-junghans.blogspot.com/2013/01/adding-syntaxhighlighter-to-blogger.html</a>). But this will require A LOT of work too! So what I did was a bit of nasty yet effective solution:<br />
<br />
In the head code I already have this code:<br />
<br />
<pre class="brush:js"><!--SYNTAX HIGHLIGHTER BEGINS-->
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPlain.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js' type='text/javascript'/>
<script language='javascript'>
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.config.clipboardSwf = &#39;http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf&#39;;
SyntaxHighlighter.all();
</script>
<!--SYNTAX HIGHLIGHTER ENDS-->
</pre>
<br />
So I added this interval code after the last javascript line:<br />
<br />
<pre class="brush:js">setInterval(function(){
SyntaxHighlighter.highlight();
}, 5000);
</pre>
<br />
This way I will ensure that syntax highlighting is always enforced, and that it will work with infinite scrolling too.<br />
<br />
Now I can enjoy my syntax highlighting with the new dynamic view theme. <br />
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-83779974126551651242014-12-26T17:06:00.000+02:002017-04-20T11:30:32.306+02:00Hijri Date Widget and JSON API<div dir="ltr" style="text-align: left;" trbidi="on">
Here is a mini project I've been using for myself and thought it can be useful for someone out there. A simple SVG based date widget + a JSON API for Hijri date.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://thaghra.com/hijri_date/" target="_blank"><img alt="http://thaghra.com/hijri_date/" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7VMr-IB1kRQeaGBSiaWF2sdoouCX7DpxboC7Uj3gK4UMf-H_nKzkUvO9rLLV7gz3GuBKFXPuE7YoKxJaGkLgIc5rCLxK7SXL6qGFdi4hJ3AkIaPnex7IIIjpJiZAQKtqahMhH5hhT8Po/s1600/hijri_date.png" /></a></div>
<br />
It has two interfaces: widget.php and api.php. With some features to make them a bit customizable.<br />
<ul>
<li>Responsive widget to fit in different sizes.</li>
<li>Customizable color for widget.</li>
<li>A JSON API for services.</li>
<li>Ability to choose computer calculations or official date by Egyptian Dar-Aliftaa.</li>
<li>An offset for fine tuning errors.</li>
<li>Choise of month name or number.</li>
</ul>
To read more about the the Hijri widget and API, please refer to the docs: ---- (no longer hosted, please refer to the <a href="https://github.com/MahmoudAdly/hijri-date" target="_blank">GitHub repository</a>) ----</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-308361232795615929.post-1442642665154302852014-07-31T16:33:00.000+02:002014-07-31T16:38:52.111+02:00PalMap - Get Palestine Back on Google Maps<div dir="ltr" style="text-align: left;" trbidi="on">
If you are a user of Google Maps, and you care about Palestine, and you get disgusted by the word "Israel" appearing on the map, here is a solution.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk9nmHIK74gN4EODpYREMMuspF4y8ltzM2X8caufVMLIrwHD6ftpsjLU7nWGQwtRJiaB1J-nYrbjao5_0AYox9l_YvViO_6uGS73reMHvWXGjaIf-s34RZsDKz7cHlEXpGOt6xlJtuCYo/s1600/no-palmap.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk9nmHIK74gN4EODpYREMMuspF4y8ltzM2X8caufVMLIrwHD6ftpsjLU7nWGQwtRJiaB1J-nYrbjao5_0AYox9l_YvViO_6uGS73reMHvWXGjaIf-s34RZsDKz7cHlEXpGOt6xlJtuCYo/s1600/no-palmap.png" height="320" width="320" /></a></div>
<br />
<br />
<a href="https://github.com/MahmoudAdly/palmap" target="_blank">PalMap</a> is a javascript library to get Palestine back on Google Maps. It does not require much. Just copy palmap.js file and img/palmap folder, add two lines in your html file, and Palestine is back on Google Maps!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNXk9KxVjo1c8IvX7MITjmasiCJf7NyHpowkJWfawMeaXO2W8dewgn49JSM_oe-XjhVNc1LrGPP5U0_gHQl1LI6HEhicCHAOEtWx8xg8eJzuXU87ZbELu9G55jF-otQa9HT9oKFrirRFU/s1600/example.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNXk9KxVjo1c8IvX7MITjmasiCJf7NyHpowkJWfawMeaXO2W8dewgn49JSM_oe-XjhVNc1LrGPP5U0_gHQl1LI6HEhicCHAOEtWx8xg8eJzuXU87ZbELu9G55jF-otQa9HT9oKFrirRFU/s1600/example.png" height="320" width="320" /></a></div>
<br />
PalMap is still in an early stage, aiming to reshape this part of the map and get all the names back to their original ones.<br />
<br />
For more info about the library, please go to PalMap repository on GitHub: <a href="https://github.com/MahmoudAdly/palmap" target="_blank">https://github.com/MahmoudAdly/palmap </a><br />
<br /></div>
Unknownnoreply@blogger.com0