2

How To: Creating a Private Journal and Mood Tracking App Using .NET 6, Chat GPT,...

 1 year ago
source link: https://jamiemaguire.net/index.php/2023/05/20/how-to-creating-a-private-journal-and-mood-tracking-app-using-net-6-chat-gpt-c-and-visual-studio-2022/?utm_campaign=how-to-creating-a-private-journal-and-mood-tracking-app-using-net-6-chat-gpt-c-and-visual-studio-2022
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

The Idea

This idea is the result of a tweet.  It came to me whilst out with my youngest son.

Like most kids, he often says or does funny things. It would be good to look back on these as time goes by.  A private digital photobook / journal sort of thing.

In this guide, you will see how to quickly build your own personal and private journal and mood tracking application.

Main requirements:

  • being able to supply text an image and mood for that day
  • being able to view all existing statuses for a given year

From a technical perspective:

  • quick and simple to code
  • as few dependencies as possible
  • easy to use on mobile and simple interface
  • no database back end

Technology and Tools

The following technology tools and services were used to create this:

  • .NET 6
  • Chat GPT
  • Copilot
  • Dev Tunnels
  • Font Awesome
  • JavaScript
  • Open-Source web template
  • Visual Studio 2022

I used an out-of-the-box dot .NET Core Web Project, open-source web template, ChatGPT and a new feature in Visual Studio 2022 called dev tunnels to speed up development.

The solution that follows was “designed”, written, and tested in less than 2-3 hours.

Screenshots and example code are provided with a video demo also available.

Finding a Suitable Web Template

The first thing to do was to find a suitable web template. I found a collection of open-source templates by Somanth Goudar.

I picked the Twitter clone then imported the HTML and CSS into a fresh Visual Studio solution.

I used placehold.co to embed image placeholders to get a feel for how each element would look.

Font Awesome

When I was happy with the overall look. Some icons were needed to supply the mood and an image.

To display emoji and file upload icons, it was over to Font Awesome and to search for 3 emojis and a file/image upload icon:

At this point, all user interface components are available for use.

Modelling a Status

A view model is needed to model the data in the user interface and that must be saved to JSON.

The following is used:

namespace StatusUpdater.Models
public class StatusViewModel
public int id { get; set; }
public string Status { get; set; }
public string UserName { get; set; }
public string UserImage { get; set; }
public string Image { get; set; }
public string Emotion { get; set; }
public DateTime DateTime { get; set; }

Each of the field are self-explanatory.  The ID field is not used but is a placeholder and gives the option of adding a unique identifier for each status.

The image property will be a base 64 string that contains the binary for an uploaded image.

The property emotion will contain one of three values to represent each mood.

Using Chat GPT to Generate Serialization Code

The following prompt was used:

The output:

Creating View Models to Represent a Timeline and Status

The entire timeline and status can be represented by using a single view model.  A  viewmodel TimeLine is created for this purpose:

This contains 2 properties. The status to post and a generic list of status view models.

Creating a Service Class to Encapsulate Code

I like to encapsulate code in APIs or service classes. A service class StatusService and is created and handles all interaction with the UI.

Adding a New Status

The Chat GPT generated code is adapted and integrated in a method AddStatus.  This accepts the view model then creates a JSON file at a (yes, hard coded, I said this was going to be quick and dirty!) path:

Lists all Statuses

The StatusService class needs updated to fetch a list of existing status in the file system.  It’s back over to Chat GPT to do this for me:

Chat GPT provides the following:

Like before, the Chat GPT code is adapted an housed in the StatusService class:

public TimeLineViewModel GetStatusModels()
string directoryPath = @"C:\Users\Jamie\source\repos\WebNotes\StatusUpdater\Data\2023"; // Replace with the directory path where the JSON files are located
string searchPattern = $"*.json"; // Replace with the desired search pattern
// Get a list of all JSON files matching the search pattern in the directory
string[] fileNames = Directory.GetFiles(directoryPath, searchPattern, SearchOption.AllDirectories);
// Create an empty list to store the deserialized StatusViewModel objects
List<StatusViewModel> statusViewModels = new List<StatusViewModel>();
// Deserialize each JSON file and add the resulting StatusViewModel object to the list
foreach (string fileName in fileNames)
string jsonString = File.ReadAllText(fileName);
StatusViewModel statusViewModel = JsonConvert.DeserializeObject<StatusViewModel>(jsonString);
statusViewModels.Add(statusViewModel);
// Convert the list to an array and do whatever you need to do with it
TimeLineViewModel timeLineViewModel = new TimeLineViewModel();
timeLineViewModel.Statuses = statusViewModels.OrderByDescending(s => s.DateTime).ToList();
return timeLineViewModel;

At this point, the StatusService class will let us add a status and fetch a list of all existing statuses.

Creating the UI

The user interface for this application is extremely simple.  It contains a single view which references a partial view that is bound to the list of existing statuses.

Status.cshtml

This used the view model TimeLineViewModel.  This is used to post a new status. It captures the text, emoji, and optional image:

<div class="row">
<div class="col-sm">
<!-- tweetbox starts -->
<div class="tweetBox">
@using (@Html.BeginForm("PostStatus", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
<div class="tweetbox__input">
<img src="@Model.UserImage" />
@*<input asp-for="@Model.StatusToPost.StatusHeader" type="text" placeholder="Status Title" maxlength="50"/>*@
<input asp-for="@Model.StatusToPost.Status" type="text" placeholder="What's happening?"
maxlength="512" style="overflow-wrap: break-word;" />
<div>
<i id="smile" class="fa-regular fa-face-smile fa-lg" style="cursor: pointer" onclick="setMood('smile')"></i>
<i id="meh" class="fa-regular fa-face-meh fa-lg" style="cursor: pointer" onclick="setMood('meh')"></i>
<i id="frown" class="fa-regular fa-face-frown fa-lg" style="cursor: pointer" onclick="setMood('frown')"></i>
@if (string.IsNullOrEmpty(Model.StatusToPost.Image))
<i id="uploadImage" class="fa-solid fa-image fa-lg" onclick="selectImage()" style="cursor: pointer"></i>
<input id="Status-Image" name="StatusImage" type="hidden" />
<input id="Status-Emotion" name="StatusEmotion" type="hidden" />
<input type="file" id="imageInput" style="display:none;">
</div>
</div>
<button type="submit" class="tweetBox__tweetButton">Post</button>
</div>
<!-- tweetbox ends -->
</div>
</div>

Note: the html attribute enctype = “multipart/form-data” })) must be set for the base64 string to be accepted by the controller.

The rendered UI for posting a status looks like this:

Directly beneath this html, the list of existing statuses is displayed.

_Status.cshtml

The partial view _Status is used to render all statuses that belong to the Statuses property in the TimeLineViewModel:

public class TimeLineViewModel
public string UserName { get; set; }
public string UserImage { get; set; }
public StatusViewModel StatusToPost { get; set; }
public List<StatusViewModel> Statuses { get; set; }
public TimeLineViewModel()
StatusToPost = new StatusViewModel();
this.Statuses = new List<StatusViewModel>();

You can see the partial view code here:

@model StatusUpdater.Models.StatusViewModel
<!-- post starts -->
<div class="post">
<div class="post__avatar">
@switch (Model.Emotion)
case "smile":
<i id="smile" class="fa-regular fa-face-smile fa-lg" style="color:green"></i>
break;
case "meh":
<i id="meh" class="fa-regular fa-face-smile fa-lg" style="color:orange"></i>
break;
case "frown":
<i id="frown" class="fa-regular fa-face-smile fa-lg" style="color:red"></i>
break;
</div>
<div class="post__body">
<div class="post__header">
<div class="post__headerText">
<h3>
@Model.UserName
<span class="post__headerSpecial">@Model.DateTime;</span>
</h3>
</div>
<div class="post__body">
@Model.Status
</div>
</div>
@if (!string.IsNullOrEmpty(Model.StatusImage))
<img src="@Model.StatusImage" [email protected] />
</div>
</div>
<!-- post ends -->

In the above partial view, the status text, datetime, and any image are displayed.  The emoji is also rendered.

Capturing an Emoji Selection in the UI

To capture an emoji selection, I wrote a quick and dirty JavaScript function SetMood.

An inline style changes the font awesome behaviour for each emoji.  The cursor behaviour is changed to a pointer when the mouse hovers over them.

<i id="smile" class="fa-regular fa-face-smile fa-lg" style="cursor: pointer" onclick="setMood('smile')"></i>
<i id="meh" class="fa-regular fa-face-meh fa-lg" style="cursor: pointer" onclick="setMood('meh')"></i>
<i id="frown" class="fa-regular fa-face-frown fa-lg" style="cursor: pointer" onclick="setMood('frown')"></i>

setMood Code:

function setMood(emoji) {
var smile = document.getElementById("smile");
var meh = document.getElementById("meh");
var frown = document.getElementById("frown");
var emotionField = document.getElementById("Status-Emotion");
switch (emoji) {
case 'smile':
smile.style.color = "green";
meh.style.color = "initial";
frown.style.color = "initial";
emotionField.value = 'smile';
break;
case 'meh':
meh.style.color = "orange";
smile.style.color = "initial";
frown.style.color = "initial";
emotionField.value = 'meh';
break;
case 'frown':
frown.style.color = "red";
meh.style.color = "initial";
smile.style.color = "initial";
emotionField.value = 'frown';
break;
default:
// code block
break;

In the above function, when an emoji is clicked, the colour of the emoji will change to indicate the selection.

A hidden field Status-Emotion is also set.

This is sent to the controller when the status is posted and saved along with the other information for the StatusViewModel.

Capturing an Image Upload in the UI

The image capture is handled in a similar way.  In the interests of time, I dipped back into Chat GPT to do this.

You can see the prompt I supplied here:

I opted for base64 as it would require the least amount of effort in terms of rendering in the browser and file IO.

It can also be easily saved in a JSON object and means no additional writing/reading of an additional file to the file system.

Chat GPT then supplied this:

I adapted this to do the following:

  • added debugging information
  • set the contents of a hidden field Status-Image to contain the base64 string of the image
  • change the colour of the font awesome file upload icon to green when the image has been successfully uploaded

You can see this here:

function selectImage() {
var input = document.createElement('input');
input.id = 'fileInput';
input.type = 'file';
input.accept = 'image/*';
input.onchange = function (event) {
var file = event.target.files[0];
var reader = new FileReader();
reader.onload = function (event) {
var base64 = event.target.result;
document.getElementById('Status-Image').value = base64;
var imageIcon = document.getElementById("uploadImage");
imageIcon.style.color = "#66c144";
reader.readAsDataURL(file);
input.click();

In the above script, the hidden field Status-Image (base64 string) is sent to the controller (along with the status text).  The Font Awesome icon uploadImage is changed to green.

Testing

At this point everything is now in place on the solution can be tested.  We add a status with an image.

The image icon turns green to indicate an image upload was successful:

Click Post and the timeline refreshes with the image:

Using Dev Tunnels To Test The Mobile Experience

I plan to use this site on my mobile to capture family memories so will often access it from my mobile phone, but it’ll be secured using a dedicated IP address.

I could have used mobile emulator on the browser but want to see what it would look like on my mobile phone.

To expose the development site to the web, I used a Dev Tunnel from Visual Studio 2022.  Dev Tunnels are currently a preview feature:

Super quick to setup and use.

I sent the dev tunnel URL from WhatsApp web to myself and opened the URL:

Some minor padding issues at the footer but I can live with that.

Video Demo

You can see the application in action here:

Further Ideas

Some further ideas for the solution to make it more secure, increase the capabilities and usefulness of it:

  • Authentication and authorization – to increase security and make public
  • Remove inline CSS – to tidy up CSS
  • Text analytics – introduce Cognitive Services for Language to surface additional insights
  • Computer Vision extract common types of images and cross reference with mood to identify patterns
  • Add categories – add categories for a status update such as personal, work, exercise
  • Web dashboards – create rich charts to aggregate data using SciChart, PowerBI etc.
  • Data export – Add an export feature to allow the download of JSON files for ingesting into Power BI

Could This Be a Micro SaaS?

Yes, but a lot is required first. From a technical perspective:

  • Database backend
  • Authentication and authorization mechanism
  • Social platforms sign in (implementing call-back URLs, application review process etc)
  • Payment provider integration such as Stripe
  • Logging and error handling mechanisms

Not to forget marketing, handling customer support, feature requests, sales etc.

Further Resources and Reading

The following links and resources will help you learn more about the services and technologies used in this solution

You can find a demo of this solution on YouTube here.

JOIN MY EXCLUSIVE EMAIL LIST
Get the latest content and code from the blog posts!
I respect your privacy. No spam. Ever.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK