Script to upload users to Moodle

In my spare time, I am a maintainer of moodle powered platforms hosted by a company that offers e-learning services and this post is about uploading new students to that platform 😀

This task generally requires a CSV file filled with information about the student. The mandatory fields are username, first name, last name and email but this is just an example and some other fields may be required. The tutor or the course owner generally provides an excel file with the necessary data but since an username is needed to login and the students may not have an email account or is not a company’s policy to use personal email addresses for login, we have take alternative measures to fit the requirements.

Here are enumerated some rules imposed for standardization:

1. Username must be the first letter of the forename followed by the first surname, no spaces in between.
2. If no email account is provided, the system will provide one for the user, even if the account doesn’t exists yet.

This is how the file looks like:
[bash]
1 Pedro Pedroso [email protected] Cobranzas
2 Juan Perez [email protected] Cobranzas
3 Juan Gonzalez [email protected] Cobranzas
4 Un boludo No tiene Por ahi

[/bash]

And this is the output from the script:
[bash]
username, password, firstname, lastname, email
ppedroso, elpassword*, Pedro, Pedroso, [email protected]
jperez, elpassword*, Juan, Perez, [email protected]
jgonzalez, elpassword*, Juan, Gonzalez, [email protected]
uboludo, elpassword*, Un, boludo, [email protected]
[/bash]

And finally, the script written in Perl. It explains itself very well I think, there is no need for line to line comment 😀

[perl]
use strict;
use warnings;

open(FILE, “< $ARGV[0]"); print "username, password, firstname, lastname, emailn"; for ()
{
if ($_=~m/([a-z|A-Z]+)s([a-z|A-Z]+)/g)
{
my $name = $1;
my $lastname = $2;
$name=~m/^w/g;
my $username = lc(“$&$lastname”);
my $mail;

if (!($_=~m/[w|_|-]+@w+.(com|net)/g))
{
$mail = lc($name.$lastname).’@example.com’;
}
else
{
$mail = $&;
}

print “$username, elpassword*, $name, $lastname, $mailn”;
}
else
{
die ($_);
}
}
[/perl]

Existential crisis

Today I configured BIND from scratch and I realize that my knowledge on something as basic as DNS was a little too superficial. I tend to be perfectionist with the things related to my career but I will never master everything I like so I have to pick one of the many things in the list and try to make deep understanding on it.

What should I specialize in? I don’t know yet and I will delay the answer to avoid losing focus. In the meantime, I’ll keep posting and eventually the right answer will show itself, hopefully soon :D.

Date/Time operations

Since I am not very fanatic of software homogeneity, when I write perl scripts and I need some function that performs certain task I use commands from console whenever it’s available even knowing that could be an equivalent Perl module available out there.

When your main goal is to get things done in the least amount of time, you cannot afford the time wasted in searching a library/module when the tools are just as far as calling a external program. This doesn’t mean that your code has to look horrible, it means that there’s no need to search for a (long) piece of code to attach it to yours in order to make your program homogeneous. After all, if you know how to write clean code, you will do it even if you mix 2323423423 languages inside your perl script.

Date/Time arithmetic using Perl.

I am aware of the existence of DateTime.pm but I have no experience using it. This was not the case with date command which finally led me to skip the learning curve and pass directly to the coding job.

The problem.

From a CDR entry containing end-datetime and duration, calculate the start-time, and transform the output using the YYMMDD/HHMMSS format.

[perl]

use strict;
use warnings;

sub main()
{
my $startTime = “02/01/2011 12:02:00”;
my $secs = 3650;

print “$startTime – $secs secs (YYMMDD HHMMSS) = @{ [ CalculateDate(“$startTime”, $secs) ] }”;
print “$secs secs in HHMMSS format is = @{ [ FormatTime($secs) ] } n”;
}

sub CalculateDate($$)
{
my ($date, $secs) = @_;

$date = FormatDateTime(`date –date “$date $secs seconds ago”`);

return $date;
}

sub FormatDateTime($)
{
return `date –date “$_[0]” +”%y%m%d %H%M%S”`;
}

sub FormatTime($)
{
my ($seconds, $hours, $mins, $secs) = shift;

$hours = padLeft(int($seconds / 3600));

$mins = padLeft($hours == 0 ? int($seconds / 60) : int(($seconds – ($hours * 3600)) / 60));

$secs = padLeft($hours == 0 && $mins == 0 ? $seconds : $seconds % 60);

return “$hours$mins$secs”;
}

sub padLeft($)
{
my $value = shift;
return (length “$value” == 1) ? “0$value” : $value;
}

main();
[/perl]

And the output is

[bash]
$ perl convert.pl
02/01/2011 12:02:00 – 3650 secs (YYMMDD HHMMSS) = 110201 110110
3650 secs in HHMMSS format is = 010050
[/bash]

Now, what I did not found searching the web was an easy method to convert seconds to a hour:minute:second format. This forced me to write my own algorithm which, hopefully, is bug free 😀
[perl]
sub FormatTime($)
{
my ($seconds, $hours, $mins, $secs) = shift;

$hours = padLeft(int($seconds / 3600));
$mins = padLeft($hours == 0 ? int($seconds / 60) : int(($seconds – ($hours * 3600)) / 60));
$secs = padLeft($hours == 0 && $mins == 0 ? $seconds : $seconds % 60);
return “$hours$mins$secs”;
}
[/perl]

What would a portability freak say?

It would probably say that my solution ignores the portability features of Perl. Yes, it is true but I don’t really care since my target is, and will always be, Unix based machines.