End of life for a couple of oldies

End of life for a couple of oldies

​Before disposing of old pcs or laptops I always remove the hard-disk which I sometimes keep (just in case) but otherwise I take a hammer to.

These two ancient beauties have now been hammered.​​

My not-first Fortran code

Back in 2001, for a challenge, I taught myself part of the Python programming language and wrote a program based on the idea of a Fortran program I wrote decades ago. Now, for another challenge, I’ve written the same program – in Fortran! I had to dig out a free compiler from the Internet (Fortran 90). Things have developed and changed since I was a programmer and I have had to learn new things in the language. Back then I think I used Fortran 66 and Fortran 77.

It’s been fun coding in Fortran, again! For the record, I downloaded and used a free product Code::Blocks, which I’ve found to be successful. I also found this YouTube video to explain configuring of Code::Blocks for Fortran.

! ===================================================================
! Program REMINDER
!
! Decades ago I wrote a FORTRAN program to take a file of reminders 
! and to display the records in a sorted and pretty format.
! In March 2021, for fun, I wrote a Python version of the program.
! Now, in January 2023, for fun, I have again written a FORTRAN version.
!
! Version 0.1 20-Jan-2023 Start of conversion of the Python code
! Version 1.0 29-Jan-2023 Final version.....?
!
! ===================================================================
program Reminder

use ReminderModule ! All the functions/subroutines are in this module.
implicit none
type (rec_data) ReminderTable(100)
character myfile*100
integer Nreminders

myFile = "C:\Users\Mike\Documents\Documents\MyFortranCode\MyReminderProject\Reminder.dat"

call GetReminderData(myFile,ReminderTable,Nreminders)
call SortList (ReminderTable,Nreminders)
call PrintList(ReminderTable,Nreminders)
end program Reminder
!
!==========================================================
! Module REMINDERMODULE containing all the functions and subroutines.
!==========================================================
module ReminderModule
!
implicit none
type rec_data !Structure to hold the reminder records
    character*11  rec_date      !reminder date
    character*50  rec_event     !reminder event
    integer  rec_numdays        !Calculate days between date and today's date
end type rec_data
contains

!============================================================
!Subroutine GETREMINDERDATA to get the reminder records
!Records are of the form dd-mmm-yyyy,"Event description" and are unordered
!============================================================
subroutine GetReminderData(filename,ReminderTable,N)
implicit none
integer mystatus,N,ans,ans_today
character filename*(*), mystatusmessage*200,mydate*11,mytext*50,today*8
character monthchar*3,months*36
integer d,m,y
type (rec_data) ReminderTable(*)
data months/"JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"/

open (unit=1,file=filename,action='read', &
      form='formatted',iostat=mystatus,iomsg=mystatusmessage)
if (myStatus /= 0) then
    write (*,'(///2a)') "OOPS! ", mystatusmessage
    stop
endif
!
! Calculate the number of days between today and a base date
!
call date_and_time(date=today) ! Inbuilt procedure
read(unit=today,fmt='(i4,i2,i2)')y,m,d ! This used to be done using an ENCODE statement!
call getDays(d,m,y,ans_today) !Get the number of days between today and a base date
!
! Get the reminder records
!
N = 0
do
    read (unit=1,fmt=*,iostat=mystatus,iomsg=mystatusmessage)mydate,mytext
    if (mystatus /= 0) then
        exit
    else
        N=N+1
        ReminderTable(N)%rec_date = mydate
        ReminderTable(N)%rec_event = mytext
        read (unit=mydate,fmt='(i2,1x,a3,1x,i4)')d,monthchar,y ! Get the date components
        m = index(months,uppercase(monthchar))/3+1 ! Lookup the month number using the month text
        call getDays(d,m,y,ans) !Get the number of days between the date and a base date
        ReminderTable(N)%rec_numdays = ans - ans_today !Number of days between date and today's date
    end if
end do
close (unit=1)
end subroutine

!============================================================
! Subroutine GETDAYS to calculate the number of days between
! the supplied date and an arbitrary base date (01/01/2000)
!============================================================
subroutine getDays( day,month,year ,ans)
implicit none
integer ans,i,daysPerMonth(1:12),daysPerMonthLeapYear(1:12)
integer day,month,year
data daysPerMonth         / 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 /
data daysPerMonthLeapYear / 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 /

ans=0
do  i = 2000, year-1 ! Add up the days in the preceding years of the supplied date
    ans=ans +365
    if (isLeap(i)) ans=ans + 1
end do
!
do i =1,month-1,1 ! Add up the days in the preceding months in the year of the supplied date
    if (isLeap(year)) then
        ans = ans + daysPerMonthLeapYear(i)
    else
        ans=ans + daysPerMonth(i)
    end if
end do
!
ans = ans + day - 1 ! Finally add in the number of days (less 1) of the month of the supplied date
!
end subroutine getdays



!=============================================================
! Function ISLEAP to determine if a year is a leap year
!=============================================================
logical function isLeap(Y)
!
integer Y
!
! A leap year is divisible by 400 or, is divisible by 4 but not by 100
!
isLeap = (mod(Y,400) .EQ. 0) .OR. (mod(Y,4) .EQ. 0 .AND. mod(Y,100) .NE. 0)

end function isleap
!
!=============================================================
! Subroutine SORTLIST to sort the data in date order.
!=============================================================
subroutine SortList (ReminderTable,N)
type (rec_data) :: ReminderTable(*),temp
integer :: N,i,j
logical :: Swapped

DO j = N-1, 1, -1
    swapped = .FALSE.
    DO i = 1, j
      IF (ReminderTable(i)%rec_numdays > ReminderTable(i+1)%rec_numdays) THEN
        temp = ReminderTable(i)
        ReminderTable(i) = ReminderTable(i+1)
        ReminderTable(i+1) = temp
        swapped = .TRUE.
      END IF
    END DO
    IF (.NOT. swapped) EXIT
END DO

end subroutine
!
!=============================================================
! Function UPPERCASE to convert text to upper case.
!=============================================================
function uppercase(string)
character(len=*), intent(in) :: string
character(len=len(string)) :: uppercase
integer :: j
do j = 1,len(string)
  if(string(j:j) >= "a" .and. string(j:j) <= "z") then
       uppercase(j:j) = achar(iachar(string(j:j)) - 32)
  else
       uppercase(j:j) = string(j:j)
  end if
end do
end function uppercase
!
!====================================================
! Function PRINTLIST to nicely print the data
!====================================================
subroutine PrintList (ReminderTable,N)
type (rec_data) :: ReminderTable(*)
integer :: N, nDays, i
logical :: tChange
character*16 t1
character*75,parameter :: mySectionSeparator="---------------------------------------------------"

tChange = .TRUE.

write (*,'(/////)')
write (*,'(2x,a)')mySectionSeparator
write (*,'(2x,a)')"         Welcome to my FORTRAN Reminder program!"
write (*,'(2x,a)')mySectionSeparator
do i = 1, N
    nDays = ReminderTable(i)%rec_numdays
    if (tChange .eqv. .TRUE. .and. nDays > 0) then
        tChange = .False.
        write (*,'(2x,a)') mySectionSeparator
    end if
    if (nDays == 0) then
        t1 = "     Today    "
    else if (nDays == -1) then
        t1 = "   Yesterday  "
    else if (nDays == 1) then
        t1 = "    Tomorrow  "
    else if (nDays < 0) then
            write(unit=t1,fmt='(i4,a)')-ndays," days since" !In my days it was the ENCODE statement
    else
            write(unit=t1,fmt='(i4,a)')ndays," days until"
    end if

    write (*,'(2x,a,1x,a,1x,a,a)')ReminderTable(i)%rec_date,t1,ReminderTable(i)%rec_event

end do
write (*,'(2x,a///)')mySectionSeparator
end subroutine

end module

And the output is….

The orange birthday present

The family were at a pub for my wife’s birthday. I mentioned this to the waitress when we ordered our puddings, and when they came out so did a candle with two waitresses singing ‘Happy Birthday’. And then we all started singing, and then also the diners in our section, much to my wife’s embarrassment! A few minutes later one of the diners came over to our table and gave my wife this orange thingy he’d made using a 3D printer. He was a very chatty guy, a Geordie. I got the impression he occasionally gave these away.

The video on the right shows you how complex an object can be manufactured using 3D printers.

My new laptop keyboard is horrid

So what don’t I like about my new laptop keyboard? It’s got a numeric keypad, that’s what. The Delete, Backspace and Enter keys should be on the extreme right of the keyboard not stuck in the middle. Unless you’re entering a lot of numeric data there’s no point in having the number keys on the right. I won’t make the same mistake with my next laptop! It doesn’t help that I’m still using the old laptop as well as the new one, so switching between the two leads to a lot of keystroke errors on the new laptop.

Grrrr….

NEW laptop

OLD laptop

My ailing laptop (part 1)

The broken screen

My 3-year-old laptop is falling apart. One of the screen’s two hinges is broken and it’s no longer possible to close the laptop. Also the screen is coming away from the actual frame. It appears to be a common fault with this particular Dell laptop.

On YouTube I found a couple of videos that show the procedure for replacing hinges – it’s not easy and essentially requires completely dismantling the laptop. The removal of a dozen components and millions of screws (and putting it back together) is not for the faint-hearted. I’m a software guy, I don’t do hardware! With this in mind I decided to postpone attempting a repair and instead opted for a new laptop (a Dell!) and perhaps attempting a repair on the old one at some future time.

The failing battery

So now I have two functioning laptops, the old one with the screen hanging off, and the nice, new, shiny one. But suddenly the battery on the old laptop has started to generate warning messages about how it’s about to fail. I’m hoping that a failed battery means that a laptop will continue to work from mains power, but it’s a bit of a nuisance having a permanently trailing power cable.

With a dodgy hinge and a failing battery one might consider writing off the old laptop, but it’s still functioning and it’s a high-spec machine, so I decided to search for a replacement battery.

Replacing a laptop battery

Dell don’t stock replacement batteries so it’s necessary to take a chance on a compatible battery. Choose from laptopbattery.co.uk, batteryempire.co.uk, replacement-batteries.co.uk and several others. There’s no way of knowing the quality of their products, who makes them, whether they’re safe, whether they hold their charge. It’s pot luck. I went for batteryempire.co.uk. I thought it was based in the UK. The log from the delivery company, UPS, show it’s not a UK company, it’s Polish. I’m not happy and in two minds whether to cancel the order and even to write off the £40 cost of the item. I decided to wait for the item to be delivered and take it from there. I may just decide to live with a failing / failed battery and only fit the replacement battery if absolutely necessary. It worries me that I don’t know whether the new battery will be safe. I’m open to any suggestions / recommendations.

My first Python code

For a challenge I’ve tried to teach myself the Python programming language. It’s a big language with many features which I, as an ex-programmer from many decades ago, am unfamiliar with. But I’ve managed to write and test the code below, though I’m not sure I want to take this much further. I’ll see…..

import csv
from datetime import datetime
from operator import attrgetter
#======================================================
# A re-creation of my Reminder program from several decades ago!! My first Python program!
#
# Version 0.1 04-Mar-2021 In the beginning
# Version 0.2 07-Mar-2021 In the beginning 
# Version 0.3 07-Mar-2021 I'm finally happy!
#======================================================
class Reminder:
    def __init__(self, myRec):
        self.Date = myRec[0]
        self.DateTimeConversion = datetime.strptime(myRec[0],"%d-%b-%Y")
        self.Message = myRec[1]
#======================================================
# Function GetReminderData to get the reminder data
#
#
# Read each record in the file
# Ignore any blank lines 
# Add each record to the list myReminders
#
def GetReminderData(myFile,myReminders) :
    with open(myFile) as csv_file:
        csv_reader = csv.reader(csv_file, delimiter=',')
        line_count = 0
        for rec in csv_reader:
            if rec == []:
                pass
            else:
                line_count += 1
                p = Reminder(rec)
                myReminders.append(p)    
    csv_file.close()
#====================================================
# Function HowManyDaysDifference to get the number of days between today's date and a text date
# This is my weird code - what a palaver!
def HowManyDaysDifference(TextDate):
    today_object = datetime.now()
    mydate_in_datetime = datetime.strptime(TextDate,"%d-%b-%Y")
    tdiff = mydate_in_datetime - today_object
    diff_in_days = tdiff.days
    if diff_in_days >= 0:
        diff_in_days += 1
    else:
        tdiff = today_object - mydate_in_datetime
        diff_in_days = -tdiff.days
  
    return diff_in_days
#==================================================
# Function PrintList to nicely print the data
def PrintList ():
    mySectionSeparator = "-" * 75
    tChange = True
    print ("\n" * 10 )
    print ("         Welcome to my Python Reminder program!")
    print (mySectionSeparator)
    
    for i in sorted(myReminders, key = attrgetter('DateTimeConversion')):
        nDays = HowManyDaysDifference(i.Date)
        if tChange and nDays > 0:
            tChange = False
            print (mySectionSeparator)
        if nDays == 0:
            t1 = "     Today    "
        elif nDays == -1:
            t1 = "   Yesterday  "
        elif nDays == 1:
            t1 = "    Tomorrow  "
        elif nDays < 0:
            t1 = '{:4d}'.format(-nDays) + " days since"
        else:
            t1 = '{:4d}'.format(nDays) + " days until"
        t2 = i.Date +t1
        print (t2,i.Message) 
    
    print (mySectionSeparator)
#=================================================
# This is the MAIN  program
#
myFile = 'C:/Users/Mike/Documents/Documents/MyPythonCode/Reminder.dat'

myReminders = []

GetReminderData(myFile, myReminders)

PrintList()

And the output is….

How to Remotely Troubleshoot Your Relative’s Computer

I have a friend who occasionally rings me up with his computer problems. Yesterday he called to say his annual anti-virus licence was going to expire that day. He confessed to having ignored the renewal reminders!

My experience with renewing McAfee anti-virus licences is that a) renewing from McAfee is ridiculously expensive, and b) not renewing from McAfee is never straightforward. My friend took my advice and went for the second option and purchased a McAfee licence from another company (InterSecure.co.uk). Inevitably the update wasn’t straightforward and wasn’t successful, hence his call for my assistance. In normal times I would probably have gone to my friend’s home, but these are not normal times.

I searched the web for how to remotely take control of someone’s computer and came across a very helpful page on the PCMag site “How to Remotely Troubleshoot Your Relative’s Computer“. Although I’ve had a career in IT support I’d never needed to do this before and this web page proved a godsend. The section on using a Windows 10 PC to take control of another Windows 10 PC was very straightforward and uses the Quick Assist tool (found under Windows Accessories). Having taken remote control of my friend’s PC I was able to install the new licence for his anti-virus software, though it wasn’t straightforward!!😎


Claude Shannon

I’m currently reading “The Idea Factory – Bell Labs and the Great Age of American Innovation” by Jon Gertner. It’s a Christmas present from my Berlin son and what a great choice it was! Bell Labs became an enormous laboratory for developing ideas and inventions at the start of the communication, information and technology industries we now take for granted. This book tells the history of Bell Labs and the leading engineers, scientists and managers.

Claude Shannon was one of those scientists and who has become known as ‘ the father of information technology’. I have a vague recollection of hearing about his work whilst I was studying for a computer science course. Now, some 50 years later, he appears in this very readable history of Bell Labs. Brilliant man that he was, it’s prompted me to look for a biography, and “A Mind at Play: How Claude Shannon Invented the Information Age” by Jimmy Soni and Rob Goodman has been well reviewed.

The biography is now on order from Postscript Books, a new mail-order company to me and which had the best price. I’ve added them to my menu of Links / Amazon alternatives. Their About page says “Most of our books are publishers’ overstocks and backlist titles…..Postscript has developed over the last 30 years, starting in south-west London in 1987 and then moving to south Devon in 2011“. An interesting business to find.


mm/dd/yyyy

I have been known to put in a false date of birth when creating a website account. Sometimes there’s no good reason why a particular organisation should have this piece of key information.

So when my DOB was rejected when trying to log into an old, redundant Apple account (so I could remove it), I wasn’t too surprised.

But after several abortive attempts at alternative solutions to the problem I discovered that my DOB was acceptable if I entered the day as the month and the month as the day. ie My DOB had been recorded as the 4th November rather than the 11th April.

I guess it’s an American v British date thingy.

The IBM card punch

The IBM card punch is my all-time favourite machine! Its purpose was to punch holes in cards that would subsequently be read by a computer and interpreted as either instructions or data. There was a lovely clunky feel to the keyboard and a sequence of clicking sounds as the current card was ejected and another one brought down ready to be punched.