Showing posts with label Software Dev. Show all posts
Showing posts with label Software Dev. Show all posts

Monday, 1 April 2024

S3 File Editor App: Integrating Python with AWS

In this post, I illustrate how I tackled the problem of making a specific application in Python that would communicate with AWS services. 

You can find the code in this repository: https://github.com/gianlucaballa/s3-file-editor

Task: Create an application that allows users to effortlessly share and modify written information such as materials, quantities, and important notes with one another.

The application needs to meet the following criteria:

  • Any user should be able to open the application on a pc with a double-click.
  • Upon opening the application, users should have immediate access to a simple text file for viewing, editing, and saving (structure for the text file is not necessary – a blank text file suffices).
  • The user can read, modify, and save the text file for other users to use.

This application will be specifically used by building engineers to easily share information such as material to buy, quantity and similar notes regarding construction sites. 

No overheads: ease of use is vital here.

Key criteria: accessibility, seamless file handling, collaboration. 

----------------------------------------------------------------------------------------------------------------------

My solution:

At first, I thought about using RDS in AWS and creating a simple table with 3 columns for a MySQL database. I steered away from this solution because it would have over complicated the code for the execution of SQL commands, without producing any significant benefit.

Instead, I proceeded with the following steps:

  1. I created an S3 bucket in AWS using a specific user with specific permissions and credentials.
  2. I uploaded an empty text file with a specific name to the S3 bucket.
  3. I wrote a Python application using boto3*.

*(Boto3 is the official AWS SDK for Python. It provides an easy-to-use Python interface to interact with various AWS services such as S3. With Boto3, developers can programmatically manage AWS resources, automate tasks, and build applications that leverage AWS services without needing to manually configure API requests). 

This solution showed to be effective and satisfied the established key criteria, as it will be demonstrated below.

----------------------------------------------------------------------------------------------------------------------

Explanation:

By sharing and analysing the Python code that I wrote for the application (see point 3. above), you can better understand the operation of the application that I designed.

    The code (formatted with Black):

import boto3
import os


def download_file(bucket_name, key, local_filename):
    s3 = boto3.client(
        "s3",
        region_name="insert_region",
        aws_access_key_id="insert_id",
        aws_secret_access_key="insert_secret",
    )
    s3.download_file(bucket_name, key, local_filename)


def upload_file(bucket_name, key, local_filename):
    s3 = boto3.client(
        "s3",
        region_name="insert_region",
        aws_access_key_id="insert_id",
        aws_secret_access_key="insert_secret",
    )
    s3.upload_file(local_filename, bucket_name, key)


def edit_file(local_filename):
    os.system(f'notepad "{local_filename}"')


def main():
    bucket_name = "insert_bucket_name"
    key = "insert_file_name.txt"
    local_filename = "temp_file.txt"

    # Download the file from S3.
    download_file(bucket_name, key, local_filename)

    # Allow the user to edit the file.
    edit_file(local_filename)

    # Upload the modified file back to S3.
    upload_file(bucket_name, key, local_filename)

    # Clean up the temporary file.
    os.remove(local_filename)

    print("Success!")


if __name__ == "__main__":
    main()

import boto3
import os

These import the "boto3" module and the "os" module. The "os"  module provides a way of using the operating system dependent functionality. For example, it allows the application to execute a command in the system's shell.
 
def download_file(bucket_name, key, local_filename):
    s3 = boto3.client(
        "s3",
        region_name="insert_region",
        aws_access_key_id="insert_id",
        aws_secret_access_key="insert_secret",
    )
    s3.download_file(bucket_name, key, local_filename)
 
This function called download_file takes 3 arguments. 
Once called, it creates an S3 client object, in other words, an interface through which your Python code can communicate with Amazon S3. In order to do this, it is necessary to specify the region in which the S3 exists, the AWS access key and secret access key of the AWS user (see point 1. above).
 
Hardcoding keys is never a good idea because they can be seen by anyone looking at the code! I hardcoded them to test the application.
 
Then the download_file function downloads the file from the specified S3 bucket (bucket_name) with the specified key/name of the file (key) to the local file (local_filename) on the file system using the function (usually the local file is where the file with the code is on the pc). s3.download_file(bucket_name, key, local_filename) is a specific function provided by Boto3.
 
def upload_file(bucket_name, key, local_filename):
    s3 = boto3.client(
        "s3",
        region_name="insert_region",
        aws_access_key_id="insert_id",
        aws_secret_access_key="insert_secret",
    )
    s3.upload_file(local_filename, bucket_name, key)
 
This is a similar function but now it uploads the local file (local_filename) on the file system using the function to the specified S3 bucket (bucket_name) with the specified key/name of the file (key). s3.upload_file(local_filename, bucket_name, key) is a specific function provided by Boto3.
 
def edit_file(local_filename):
    os.system(f'notepad "{local_filename}"')
 
The edit_file function opens the specified file (local_filename) in the default text editor of the system using the os.system function. Since I am testing this application in Windows, I specified "notepad" as the default text editor.
 
def main():
    bucket_name = "insert_bucket_name"
    key = "insert_file_name.txt"
    local_filename = "temp_file.txt"
 
    # Download the file from S3.
    download_file(bucket_name, key, local_filename)

    # Allow the user to edit the file.
    edit_file(local_filename)

    # Upload the modified file back to S3.
    upload_file(bucket_name, key, local_filename)

    # Clean up the temporary file.
    os.remove(local_filename)

    print("File has been updated and uploaded to S3.")
 
In Python, main() is a conventional name for the main entry point of a script or program. It's a function that typically contains the main logic or sequence of actions that the script should perform when executed.The purpose of defining a main() function is to encapsulate the main functionality of the script and to provide a clear starting point for the program's execution. This makes the code more modular and easier to understand, as the main logic is isolated within a specific function. 
 
In this case, main() is used to call the functions that I defined above in a specific order (download, edit, upload), clean up the temporary file on the pc once it is uploaded to S3 (using os.remove(local_filename)) and print a message once the process is concluded. In main(), I also establish variables that will be used as arguments for the functions: the name of the S3 bucket (see point 1. above), the name of the .txt file that I initially uploaded to S3 (see point 2. above) and the local file name (temp_file.txt works fine for this).

if __name__ == "__main__":
    main()

This final block ensures that the main() function is executed only when the script is run directly, not when it's imported as a module into another script. This allows the script to be used both as a stand-alone program and as a reusable module. 
 
Final notes:
  • Ensure that the IAM user associated with the AWS credentials has the necessary permissions to access the specified S3 bucket and perform read/write operations on the objects.
  • Make sure to handle sensitive information such as AWS access keys securely.
  • This script is intended for educational purposes and can be modified to suit specific requirements. 

----------------------------------------------------------------------------------------------------------------------

To transform the script into a clickable .exe, I can use

pyinstaller --onefile my_script_name.py
After PyInstaller finishes, it will create a "dist" directory in the script's directory, containing the executable file. This executable can be distributed to others.

Monday, 2 October 2023

Branches in Git

In the previous post on Git, changes have been made directly on the main branch*.

*(The “master” branch was the default branch name for any git's repository. This has been changed to “main” to promote a more inclusive language).

Let’s see how to set up new branches.

(Thanks to Kevin Stratvert for the tutorial 'Git and GitHub for Beginners Tutorial', available here).

Before we do that, what is a new branch?

It is a copy of the main branch in which we want to modify single aspects of our code without affecting the main branch. Once all the necessary changes have been made, we can merge it back into the main branch.

In other words, you can create a new branch for a new feature or for a bug fix etc. without affecting "main". 

Source

To create a new branch, type this on your terminal:

git branch NameOfTheChange

"NameOfTheChange" was made up. It's just the name of the new branch. 

PS: make sure to be in the right directory where the repository is! E.g.: cd C:\Users\etc.

git branch -> to check how many branches have been made. The * sign tells us in which branch we are currently operating.

For the command above, I used the PowerShell terminal in Visual Studio Code. I discovered that my "main" branch is called “master” by my terminal, despite having specifically named it "main" when I previously set it up. If this is the case, if you are in the "master" branch, use git branch -m main (-m stands for "move/rename").

git switch NameOfTheChange -> to switch branch.

The command git status displays information about the current state of my working directory and the Git repository. As expected, it also informs us about which branch we are currently operating in.

Let’s see the branch in action. I am expecting to make a change to my branch without affecting any other branch.

I have a text file (.txt) in my repository, so I modify and commit it. I proceed in this order:

1) I modify the file manually while being in "NameOfTheChange". Save it.

2) git status -> to verify the modification.

3) git commit -a -m “description of what I am doing” -> to commit the change.

Remember that -a is to force commit for a change we haven’t staged. -m is used to add comments to the commit.

If I now switch the branch, from “NameOfTheChange” to “main”, I expect the text file in the repository to not show my last change. The repository should change according to which branch I am currently working with. I type the switch command:

git switch main

Note: Watch out if your “main” is called “master” like in my case!
 
I open the repository and the text file for "main". It should not display my last change that I made in “NameOfTheChange”. However, both branches display the same text file despite having specifically changed it for one of my branches only.
 
I need to investigate what the issue is.

I created a new file in my repository called text.txt with random words in it. I am in the “NameOfTheChange” branch. I commit it in that branch but, again, both branches display this file. I was expecting only “NameOfTheChange” to display it.

I tried the same process, but I accidentally hit some wrong button on my terminal during the commit. Now any further commit makes me display the following error:

'Another git process seems to be running in this repository, e.g.
an editor opened by 'git commit'. Please make sure all processes
are terminated then try again. If it still fails, a git process
may have crashed in this repository earlier: remove the file manually to continue.' 

This problem occurs when two git commands are executed simultaneously (e.g., one from the IDE and one from the command prompt, or when a second command is given while the first one is still executing). To solve this, I proceeded in this way:

1) I accessed the hidden folder of Git (.git) in the repository. 

2) I delete the "index.lock" file*. 

3) I checked back my git status. 

4) I properly committed the previous changes (adding a comment too!)

*(‘When you perform a Git command that edits the index, Git creates a new index.lock file, writes the changes, and then renames the file. The index.lock file indicates to other Git processes that the repository is locked for editing.’ Source).

I noticed that changes on .htm files in my repository are identified by Git, while changes on .txt files are not (in my case, I simply opened a .htm file with Notepad and modified it). I am still looking for an explanation for this behaviour. In conclusion, I tested the branches by modifying a .htm file in one branch and committing the change in that branch only. When I switched the branch I noticed that, as expected, in this last branch the same .htm file had no changes. In this way, I demonstrated that branches are independent: code can be modified in one branch without affecting the same code in other branches. 
 
SOLUTION: In my previous post, I made a .gitignore file which instructed Git to ignore all .txt extensions. These files are then not reported by git status.
 
---------------------------------------------------------------------------------------------------------------------- 

Once we’re happy about the changes, we can merge the new branch with our “main”.

git merge -m “Type a comment here” NameOfTheChange

“NameOfTheChange” is the branch to merge with “main” (you must be in “main” to do it, use git switch).

I can delete now the “NameOfTheChange” branch:

git branch -d NameOfTheChange

-d stands for "delete".
 
Use git branch to confirm that the “NameOfTheChange” branch is no more.

git switch -c NameOfABranch -> create a new branch and automatically switch to it (-c stands for “create”).

When you merge a branch with “main” and both have the same file with different text or code, Git will report a CONFLICT, the type of conflict (e.g., content), and for which file. We must resolve the conflict (e.g., manually) before the merge can take place.

Wednesday, 23 August 2023

An Introduction to Git

My first post on this blog is about Git, one of the most popular version control systems which is used in many software development projects.

Git is a critical tool for managing and collaborating on software projects, because it allows multiple developers to work on the same codebase* simultaneously while keeping track of changes, versions, and history.

*(In other words, the collection of components such as source code, scripts etc. which make up a software application).

While studying Git, I came across some concepts that are unfamiliar to me such as Version Control Systems, Distributed Version Control Systems and Source Code Management.

Let’s delve into them. 


Version Control Systems (VCS): software tools that help track changes to files and code over time. They also cover large binary files and digital assets.

Distributed Version Control Systems (DVCS): A type of VCS that allows every contributor to have all the history of the code repository. Apparently, it is a more decentralised system: for example, contributors can work offline and have access to the full history without relying on a central server.

Source Code Management (SCM): like VCS but only used to manage the source code.

Git is both a DVCS and an SCM!

----------------------------------------------------------------------------------------------------------------------

(Thanks to Kevin Stratvert for the tutorial 'Git and GitHub for Beginners Tutorial', available here).

I proceed to install Git from here and notice that it comes with a terminal called Git Bash. I assume that I can use another terminal that I have on my computer such as Command Prompt rather than Git Bash. Indeed, if I type the command

git config -h

on Command Prompt, it displays the help documentation for Git. Another useful command is

git help config

As suggested by tutorials, before I start using Git, I should specify my name and email address to associate an identity to my commits*.

*(The saving of a set of changes one has made to the project's source code or files; I think of it as a sort of snapshot!).

These are the configurations that I am going to insert in Command Prompt

git config --global user.name "Gian"

git config --global user.email "my.email@example.com"


Then I define a branch, in other words a separate "copy" of the project that might allow me to work on new features, bug fixes, or experiments without affecting the main codebase. I decided to call my branch, “main”.

git config --global init.default branch main

To create a repository (“repo”), I identify a path on my pc where I keep the files that can make up a website. That will be the location for my repository. I first go to the chosen directory

cd C:\Users\etc.

Then

git init

In this way, I created a location where all the source code, project files, and historical changes related to a software project are stored and tracked. I notice that a hidden folder .git is created in my chosen directory; this probably contains necessary components to make Git work (like the heart of the repository).

I believe that if I were collaborating with other developers, this location should have been on a hosting service such as GitHub but I am unsure.

The command git status displays information about the current state of my working directory and the Git repository. In my case, for “C:\Users\etc.”, it displays the name of my branch (“main”) and specifies that there are no commits yet. It also lists the files I put in “C:\Users\etc.” and calls them "Untracked" (in a red font). These are actually not monitored by Git: 

  • Git does not track changes or history for Untracked files.
  • These files do not appear in the commit history, and Git doesn't automatically include them in commits.
At this point I feel confused about where the branch information is saved and I try the command “git status” on another terminal, Git Bash. The result is: “fatal: this operation must be run in a work tree”.

Maybe, I need to run it on the same folder, so I repeat

cd C:\Users\etc.

It doesn’t work. Let’s redo

git init

And now it works. But why? Why has changing the terminal forced me to repeat the initialisation process for the specified path? I assumed that specifying the path was enough. Anyway…

I want to Track an Untracked file called file.htm which is in “C:\Users\etc.”, as the status reported.

git add file.htm

I re-check the status with git status. That specific file is now in a green font and not in red, and it is preceded by instructions about how to revert this operation (I tried this suggested command to Untrack the file and checked back with git status… it works!).

I create a .gitignore file out of an empty text file and put it in my chosen directory. Here I can list all the files I want to ignore.

Example:

# ignore all .txt files
*.txt

The first line is a comment describing my intentions. The second one instructs git that I want to ignore all(*) .txt extensions. These files won’t be reported by git status anymore. I save the .gitignore file.

To put files in an environment called staging, a preparatory phase before committing (like holding the pen before writing...), I can use either

1) git add –all

or

2) git add –A

or

3) git add .

1) and 2) are the same: they stage all changes, including new files, modifications, and deletions, both for Tracked and Untracked files

3) It stages changes for Tracked files and new files but does not stage file deletions.

It is still unclear to me the purpose of staging. Couldn’t we have a direct commit without the staging?

Anyway, I am ready to commit*.

*(You can only commit what has been staged unless you force it with git commit -a). 

In my experience, I found it unwise to force commit without staging because it might cause tracking issues.

git commit -m “description of what I am doing”

-m (message) is to type the comment. If I use git status, there are no more files shown to commit.

I also found that writing a comment for each commit can be extremely useful because it helps us and the team identify the change and the reason behind it. 

----------------------------------------------------------------------------------------------------------------------

If I modify one of the files in my folder and I use the command git status, it will inform me that one file has been modified (the file goes back to the working phase before staging). git diff to display the actual change (red means old, green means new).

git add file.htm -> to stage files.

git restore --staged file.htm -> to remove a file from staging (before commit).

git restore “file.htm” -> to restore a deleted file.

git mv “file.htm” “file2.htm” -> to move (rename) a file. Remember to commit it!

git log -> to display the list of commits made.

git log --oneline -> to display log in one line.

git commit -m “new amended comment…” --amend -> to amend the last commit.

git log -p -> to display the commit history with the actual changes (diffs) introduced in each commit ("q" to quit).

git rebase -i --root -> to open the editor (":x" to quit).

Integration of Cloud Technologies with the Metaverse

The potential impact and timeline for the development of the Metaverse remain uncertain, with ongoing debate over whether it represents a me...