Skip to main content
ALPHA    This is new software undergoing tests! Thank you for your patience.

Candidate: tim osahenru tim Assessed by: Nicholas Tollervey ntoll

Python (2023) ~ Grade 5 (Higher)

dev-locate

A platform where developers can signup with their projects on display and hiring managers can make searches of developers based on location, year of experience, tech stack or any other search query.

Attached files
Filename (click to download) Size Uploaded by
Screenshot_47.png 96.8 KB tim
Markdown code
![Screenshot_47.png](/media/assessment/e2c014a6/2022-11-25/21-29-20/Screenshot_47.png "Screenshot_47.png")
Screenshot_48.png 39.2 KB tim
Markdown code
![Screenshot_48.png](/media/assessment/e2c014a6/2022-11-25/21-32-41/Screenshot_48.png "Screenshot_48.png")
Screenshot_49.png 80.1 KB tim
Markdown code
![Screenshot_49.png](/media/assessment/e2c014a6/2022-11-26/01-26-13/Screenshot_49.png "Screenshot_49.png")
2022-11-28_20-06-31ipad.mp4 10.6 MB tim
Markdown code
[2022-11-28_20-06-31ipad.mp4](/media/assessment/e2c014a6/2022-11-29/15-46-55/2022-11-28_20-06-31ipad.mp4){target="_blank"}
profile.gif 1.7 MB ntoll
Markdown code
![profile.gif](/media/assessment/e2c014a6/2022-12-03/17-19-41/profile.gif "profile.gif")
project.png 74.8 KB ntoll
Markdown code
![project.png](/media/assessment/e2c014a6/2022-12-03/17-26-39/project.png "project.png")
homepage.png 270.7 KB ntoll
Markdown code
![homepage.png](/media/assessment/e2c014a6/2022-12-03/17-42-05/homepage.png "homepage.png")
Screenshot_57.png 44.6 KB tim
Markdown code
![Screenshot_57.png](/media/assessment/e2c014a6/2022-12-05/11-14-39/Screenshot_57.png "Screenshot_57.png")
bad.png 50.0 KB tim
Markdown code
![bad.png](/media/assessment/e2c014a6/2022-12-05/11-29-30/bad.png "bad.png")
good.png 50.1 KB tim
Markdown code
![good.png](/media/assessment/e2c014a6/2022-12-05/11-31-55/good.png "good.png")
form.png 20.6 KB tim
Markdown code
![form.png](/media/assessment/e2c014a6/2022-12-05/12-01-04/form.png "form.png")
count.jpg 739.2 KB tim
Markdown code
![count.jpg](/media/assessment/e2c014a6/2022-12-05/19-14-36/count.jpg "count.jpg")
create.jpg 712.2 KB tim
Markdown code
![create.jpg](/media/assessment/e2c014a6/2022-12-05/19-14-55/create.jpg "create.jpg")
virtual.png 34.8 KB tim
Markdown code
![virtual.png](/media/assessment/e2c014a6/2022-12-07/03-49-27/virtual.png "virtual.png")
tutorial.png 33.8 KB tim
Markdown code
![tutorial.png](/media/assessment/e2c014a6/2022-12-07/03-50-55/tutorial.png "tutorial.png")
Screenshot_88.png 271.2 KB tim
Markdown code
![Screenshot_88.png](/media/assessment/e2c014a6/2022-12-08/20-54-47/Screenshot_88.png "Screenshot_88.png")
Screenshot_86.png 363.5 KB tim
Markdown code
![Screenshot_86.png](/media/assessment/e2c014a6/2022-12-08/21-06-28/Screenshot_86.png "Screenshot_86.png")
1.png 14.2 KB tim
Markdown code
![1.png](/media/assessment/e2c014a6/2022-12-08/21-15-04/1.png "1.png")
2.png 5.3 KB tim
Markdown code
![2.png](/media/assessment/e2c014a6/2022-12-08/21-16-03/2.png "2.png")
whatsapp.png 239.1 KB tim
Markdown code
![whatsapp.png](/media/assessment/e2c014a6/2022-12-09/17-19-06/whatsapp.png "whatsapp.png")
received.png 30.1 KB tim
Markdown code
![received.png](/media/assessment/e2c014a6/2022-12-09/17-21-53/received.png "received.png")
sent.png 31.7 KB tim
Markdown code
![sent.png](/media/assessment/e2c014a6/2022-12-09/17-22-27/sent.png "sent.png")
Screenshot_101.png 160.1 KB tim
Markdown code
![Screenshot_101.png](/media/assessment/e2c014a6/2022-12-10/00-21-24/Screenshot_101.png "Screenshot_101.png")
Screenshot_102.png 23.9 KB tim
Markdown code
![Screenshot_102.png](/media/assessment/e2c014a6/2022-12-10/04-50-59/Screenshot_102.png "Screenshot_102.png")
Screenshot_105.png 41.3 KB tim
Markdown code
![Screenshot_105.png](/media/assessment/e2c014a6/2022-12-11/17-54-53/Screenshot_105.png "Screenshot_105.png")
Screenshot_105.png 41.3 KB tim
Markdown code
![Screenshot_105.png](/media/assessment/e2c014a6/2022-12-11/17-55-08/Screenshot_105.png "Screenshot_105.png")
inbox_error.png 66.3 KB ntoll
Markdown code
![inbox_error.png](/media/assessment/e2c014a6/2022-12-13/16-12-03/inbox_error.png "inbox_error.png")
Screenshot_108.png 37.0 KB tim
Markdown code
![Screenshot_108.png](/media/assessment/e2c014a6/2022-12-13/16-38-58/Screenshot_108.png "Screenshot_108.png")
Screenshot_117.png 27.8 KB tim
Markdown code
![Screenshot_117.png](/media/assessment/e2c014a6/2022-12-15/01-42-56/Screenshot_117.png "Screenshot_117.png")
git_error.png 42.0 KB tim
Markdown code
![git_error.png](/media/assessment/e2c014a6/2022-12-15/19-41-34/git_error.png "git_error.png")
sphinx_error.png 32.5 KB tim
Markdown code
![sphinx_error.png](/media/assessment/e2c014a6/2022-12-15/19-47-55/sphinx_error.png "sphinx_error.png")
sphinx_tree.png 18.9 KB tim
Markdown code
![sphinx_tree.png](/media/assessment/e2c014a6/2022-12-15/19-49-55/sphinx_tree.png "sphinx_tree.png")
readthedocs_error.png 50.8 KB tim
Markdown code
![readthedocs_error.png](/media/assessment/e2c014a6/2022-12-15/19-55-53/readthedocs_error.png "readthedocs_error.png")
complete_build.png 298.1 KB tim
Markdown code
![complete_build.png](/media/assessment/e2c014a6/2022-12-15/19-57-14/complete_build.png "complete_build.png")
message.png 43.5 KB tim
Markdown code
![message.png](/media/assessment/e2c014a6/2022-12-15/20-00-29/message.png "message.png")

Status: Closed (results delivered). 88/100 ~ Pass with MERIT (19/12/2022).

View full results


tim tim osahenru ~ 23 Nov 2022 9:51 p.m.

The hiring process of a software developer can be tough from both the developer and a hiring manager’s point of view.

When I started my Journey as a software developer late last year (November, 2021) I had no platform to share my projects without having to build a personal portfolio. So, I thought why not build a platform where other developers can signup and display different projects they have created: have developers link their projects to the GitHub repo, live URL, a description of the project and any valuable details they choose to share about their projects.

Initially built it using Django’s function based views which is a more beginner friendly approach.

As a challenge to myself I want to implement the same idea using Django’s class based views and add some more functionality like a search functionality and chat functionality; a search function where hiring managers can search for developers using any query search and also a chat session where hiring managers can chat with any developer they choose to work with.

Why? you may ask, well as a Django developer class based view is the industry way of writing effective and efficient codes. So, to strengthen my understanding of Python and the Django framework I want to explore the world of Django using its class based views approach.

The first attempt I gave to this didn’t have much functionality to it, just a signup page, a profile settings page and a page where projects can be created, updated or deleted and lastly have all projects displayed in the homepage, but this second attempt I want to expand my technical knowledge by adding the functionalities listed above.

It's going to be fun guys... "Let the games begin"


tim tim osahenru ~ 23 Nov 2022 11:24 p.m.

I have created two apps (projects and accounts) within my Django project. account app has the models.py file where I have created a model for our engineers as shown below

accounts.models

from django.db import models
from django.contrib.auth.models import AbstractUser


class Engineer(AbstractUser):
    username = models.CharField(max_length=200, null=True)
    email = models.EmailField(unique=True, null=True)
    bio = models.TextField(default='little about you...')
    # avatar =
    country = models.CharField(max_length=200)
    years_of_experience = models.PositiveIntegerField(null=True, blank=True)
    tech_stack = models.CharField(null=True, blank=True, max_length=300, default='stack_one | stack_two | stack_three')

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    def __str__(self):
        return self.username

tim tim osahenru ~ 23 Nov 2022 11:31 p.m. (updated: 24 Nov 2022 9:14 a.m.)

Similarly, in our projects module we also have a models.py where we create a model for our projects

projects.models

from django.db import models
from accounts.models import Engineer


class Project(models.Model):
    engineer = models.ForeignKey(Engineer, on_delete=models.CASCADE)
    name = models.CharField(max_length=100, default='Max 100 char')
    tech_used = models.CharField(max_length=200, default='stack_one | stack_two')
    updated = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)
    make_public = models.BooleanField(default=False)
    # image =
    description = models.TextField(null=True, blank=True)
    repo_url = models.URLField(null=True, blank=True)
    live_url = models. URLField(null=True, blank=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['created']

avatar and image field where commented out: Django image field requireas a bit of modification before they can be used, this will be visited later

Next, we build a CRUD functionality using class based views


tim tim osahenru ~ 24 Nov 2022 11:08 a.m. (updated: 25 Nov 2022 8:43 p.m.)

The beauty in building with Django’s class based views is you write cleaner codes which are reusable and horror will be that there are so many functions operating under the hood which are not comprehensible to a beginner.

Query our project model by displaying all projects that has the make_public field set to True

# ............. All Projects ...............
class AllProjects(ListView):
    model = Project
    context_object_name = 'projects'
    template_name = '../templates/index.html'

    def get_queryset(self):
        public_projects = Project.objects.filter(make_public=True)
        return public_projects

Get more details of each projects

# ............. Detail Project ...............
class ProjectDetail(DetailView):
    model = Project
    template_name = '../templates/detail.html'

Edit each project and redirect user back to the all projects once successful

# ............. Edit Project ...............
class ProjectEdit(LoginRequiredMixin, UpdateView):
    model = Project
    template_name = '../templates/edit.html'
    fields = '__all__'
    success_url = reverse_lazy('all_projects')

Create each project by assigning the logged in user to the project they have just created and once successful redirect the user to all projects

# ............. Create Project ...............
class ProjectCreate(LoginRequiredMixin, CreateView):
    model = Project
    template_name = '../templates/create.html'
    form_class = ProjectForm
    success_url = reverse_lazy('all_projects')

    def form_valid(self, form):
        if form.is_valid():
            form.save(commit=False)
            form.instance.engineer = self.request.user
            form.save()
            return redirect('all_projects')
        return super(ProjectCreate, self).form_valid(form)
# ............. Delete Project ...............
class ProjectDelete(LoginRequiredMixin, DeleteView):
    model = Project
    template_name = '../templates/delete.html'
    fields = '__all__'
    success_url = reverse_lazy('all_projects')

Next will be to create an authentication system where engineers can signup, create an account, add a project, ...


tim tim osahenru ~ 25 Nov 2022 8:46 p.m.

Hey whatsup guys.... So I modified my CRUD function; a LoginRequiredMixin has been added to ensure unauthenticated users are not able to create, edit or delete projects but do not require any authentication to view projects


tim tim osahenru ~ 25 Nov 2022 8:56 p.m.

Also moving on we have been able to also add an authentication system where Engineers can SignUp and Login to create a project

As a thing of preference I used email and password to login users as opposed to a username and password; I assume users easily remember their email address which is a unique id as to a username

# ................ Login User ........................
class LoginUser(LoginView):
    model = Engineer
    redirect_authenticated_user = True
    template_name = '../templates/login.html'

Once an Engineer has successfully created an account they redirected to a login page, if the login is successful they are redirected to home page

NOTE TO SELF: Users should be redirected to their profile page after login instead of the home page

# ............................. SignUp User ....................
class UserSignUp(FormView):
    form_class = EngineerCreationForm
    template_name = '../templates/signup.html'
    success_url = reverse_lazy('login')

    def form_valid(self, form):
        if form.is_valid():
            form.save()
            return redirect('login')
        return super(UserSignUp, self).form_valid(form)

tim tim osahenru ~ 25 Nov 2022 9:04 p.m.

Visitors(Hiring managers) can now visit the profiles of the Engineers, they find more about them through their profile and also chat them up.

# ....................... Engineer Profile .....................
class EngineerProfile(DetailView):
    model = Engineer
    template_name = '../templates/profile.html'

NB. The chat function hasn't been added yet


tim tim osahenru ~ 25 Nov 2022 9:11 p.m.

To have a clean design, Engineers should have all their projects in their profile where they can either update or delete the projects... On a second thought too, I think I should also add the create function to the Engineer's profile...

hmm... thinking!


tim tim osahenru ~ 25 Nov 2022 9:34 p.m.

Challenge:

Snug! 😩 I'm having a bug in my code Screenshot_47.png

from the project details page I am trying to take visitors to the profile page of the engineer when they click on a checkout my profile button

Screenshot_48.png

Line 9 shows something is messed up with my code.

thinking in process... 😓😓


tim tim osahenru ~ 25 Nov 2022 10:15 p.m.

Eureka! 😆😆😆

So I figured out I wasn't passing my query properly in the templates. this is how I passed it engineer.id Recall from our Project models that is projects.models we tied a relationship(ForeignKey) with every engineer so to fix the bug I just queried upwards using project.engineer.id in my templates


tim tim osahenru ~ 25 Nov 2022 10:38 p.m.

Cool! all the pieces are coming together. Next, is to query individual projects of an engineer present so it is available on their profile. The edit and delete button will only be visible to the engineers who created the projects and also only them can edit or delete a project, so you can't delete or delete what you didn't create


tim tim osahenru ~ 26 Nov 2022 1:29 a.m. (updated: 26 Nov 2022 1:32 a.m.)

I just faced another bug which I fixed, hilariously I don't understand why it worked so I'll just included both lines of codes.

Here the line of code of with the bug issue

# ....................... Engineer Profile .....................
class EngineerProfile(DetailView):
    model = Engineer
    template_name = '../templates/profile.html'

    def get_context_data(self, **kwargs):
        context = super(EngineerProfile, self).get_context_data()
        context['engineer'] = Engineer.objects.get(id=pk)
        context['public_projects'] = context['engineer'].project_set.filter(make_public=True)
        context['private_projects'] = context['engineer'].project_set.filter(make_public=False)
        return context

error message I got Screenshot_49.png

added this line of code pk = self.kwargs.get('pk')

# ....................... Engineer Profile .....................
class EngineerProfile(DetailView):
    model = Engineer
    template_name = '../templates/profile.html'

    def get_context_data(self, **kwargs):
        context = super(EngineerProfile, self).get_context_data()
        pk = self.kwargs.get('pk')
        context['engineer'] = Engineer.objects.get(id=pk)
        context['public_projects'] = context['engineer'].project_set.filter(make_public=True)
        context['private_projects'] = context['engineer'].project_set.filter(make_public=False)
        return context

Not sure why I had to declare pk as a variable I thought pk by default is passed into a DetailView please I need clarity on this


tim tim osahenru ~ 26 Nov 2022 9:03 p.m.

Hello guys, another functionality has been added, Engineers can now update their profiles from the profile page.

# ............. Engineer Settings ........................
class EngineerSettings(LoginRequiredMixin, UpdateView):
    model = Engineer
    template_name = '../templates/settings.html'
    form_class = EngineerForm

    def form_valid(self, form):
        if form.is_valid():
            form.save()
            return redirect('profile', pk=self.request.user.id)
        return super(EngineerSettings, self).form_valid(form)

tim tim osahenru ~ 28 Nov 2022 8:44 p.m. (updated: 29 Nov 2022 3:50 p.m.)

Here is a link to the github repo of the entire project

Github repo to this project


tim tim osahenru ~ 29 Nov 2022 3:42 p.m. (updated: 29 Nov 2022 10:18 p.m.)

Here is a video that highlights some of the major functionalities of the application.


ntoll Nicholas Tollervey ~ 03 Dec 2022 6:39 p.m. (updated: 03 Dec 2022 11:05 p.m.)

Tim, 👋

First of all, huge congratulations on the magnificent work done so far 🎉.

I'm going to be your mentor and, in addition to my role as assessor for your grade, I'm here to help you develop and refine your project. If at any point you have any questions, comments or require more guidance, please don't hesitate to ask here on CodeGrades. In this way the story of our collaboration on your project will unfold as evidence of your attainment.

I have some initial feedback, and please don't be disappointed by the amount of detail... this is to be expected as we start exploring your project, and it reflects the relatively advanced level of grade 5.

The important thing is for you to read and reflect then refine your project. Like I said, I'm here to help, and part of that help is to suggest ways to improve and change the project. You're also quite within your rights to disagree with me or decide to take my suggestions in an unexpected direction, so long as you back yourself up and explain what you're doing..! 👍

Developer Setup

I've looked at your source code on GitHub, cloned it so I have a local copy and tried to get it to run.

I ran into some problems, which you may wish to address.

It's usually good practice to have a "Developer Setup" section in the README to give baby-steps to a working local development environment. How do you know what to write in such a "Developer Setup" section? Well... try cloning your repository and using a clean virtual environment to see what happens, as if you were a new developer on the project. 😄 In any case, here's what I found:

django.core.exceptions.ImproperlyConfigured: Set the SECRET_KEY environment variable
SystemCheckError: System check identified some issues:

ERRORS:
accounts.Engineer.avatar: (fields.E210) Cannot use ImageField because Pillow is not installed.
    HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "python -m pip install Pillow".
projects.Project.image: (fields.E210) Cannot use ImageField because Pillow is not installed.
    HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "python -m pip install Pillow".

In summary, I was able to get your project running, but it took a bit of work and specialist knowledge. Please can you update your README with the baby-steps needed to set up a development version?

Running the project

Once I got the project running I noticed an error in the output from the local server:

Internal Server Error: /project.png/
Traceback (most recent call last):
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 2018, in get_prep_value
    return int(value)
ValueError: invalid literal for int() with base 10: '.png'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/views/generic/base.py", line 103, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/views/generic/base.py", line 142, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/views/generic/detail.py", line 108, in get
    self.object = self.get_object()
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/views/generic/detail.py", line 37, in get_object
    queryset = queryset.filter(pk=pk)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/query.py", line 1420, in filter
    return self._filter_or_exclude(False, args, kwargs)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/query.py", line 1438, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/query.py", line 1445, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1532, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1562, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1478, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1303, in build_lookup
    lookup = lookup_class(lhs, rhs)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/lookups.py", line 27, in __init__
    self.rhs = self.get_prep_lookup()
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/lookups.py", line 341, in get_prep_lookup
    return super().get_prep_lookup()
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/lookups.py", line 85, in get_prep_lookup
    return self.lhs.output_field.get_prep_value(self.rhs)
  File "/home/ntoll/.virtualenvs/dev-locate/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 2020, in get_prep_value
    raise e.__class__(
ValueError: Field 'id' expected a number but got '.png'.
[03/Dec/2022 16:45:32] "GET /project.png/ HTTP/1.1" 500 137493

Clearly the site still works, but there appears to be a problem somewhere. Can you take a look and tell me what you find..? Thank you..! 🤔

It was very easy to sign up to my locally running version. 💥 When I tried to update my profile I found the form floated to the bottom of the screen. Also, the tech-stack list feels like it should be a comma separated list. I've attached a screen-grab of this process so you can see what I mean.

profile.gif

I notice the default content for blank fields doesn't get automatically replaced when you type. There's a trick you can use in the HTML for the <input> elements in your form. Just set, the placeholder attribute like this: <input placeholder="A default value" type="text"/>. It's a little thing, but will make your site feel nicer to use. 👍

When I click on "My projects", I clearly don't have any projects... so I was expecting to see an "Add Project" button somewhere. It took me a while to find it (it's on the homepage). Although I have to say, it's really fun to see my project on the website (it looks really professional!).

project.png

There are certain aspects of the site that clearly don't (YET!) work. You also use dummy data or placeholder content. This is absolutely NOT A PROBLEM, and helps me to understand how you'd like the site to develop. Really great work. But, since you've hinted at features, I'd like to see these functioning if at all possible. This screen shot (with annotations) describes what I mean:

homepage.png

  1. Can we search by username or tech stacks?
  2. The shortcuts don't work, and the numbers are dummy data. Can you fix this so I have an inbox and the data is live..?
  3. Replace dummy data with live data.
  4. Broken profile image. Can you fix this..?
  5. Once I have an inbox, I assume new messages will appear in "Recent Activities"..? Or perhaps this is for social media activity..? A Twitter stream perhaps..?

Related to point 2. Can I also send a message to another developer..? I think with the search and messaging working (along with those other bug fixes, and live real data) you'll have a really useful and professional looking website. 🚀

I've taken a look around your code and have a few more items of feedback:

# ............. All Projects ...............
class AllProjects(ListView):
    model = Project
    context_object_name = 'projects'
    template_name = '../templates/index.html'

    def get_queryset(self):
        public_projects = Project.objects.filter(make_public=True)
        return public_projects

The convention in Python is to write comments for classes and methods inside triple quoted strings, underneath (not above) the thing you're commenting about. Like this:

class AllProjects(ListView):
    """
    All projects listed on a single page.
    """
    model = Project
    context_object_name = 'projects'
    template_name = '../templates/index.html'

    def get_queryset(self):
        public_projects = Project.objects.filter(make_public=True)
        return public_projects

Also, something for you to think about... in the example code immediately above, there is a get_queryset method. In your code you have two lines, but how might you make it just one line..? (Hint: a version with just one line would start with return). I'm asking this because it's always good to write less code if possible, unless more code makes it understandable. Don't hesitate to ask if this question isn't clear. 👍

Once again, huge congratulations on getting such an impressive first draft of your project ready for feedback. I really enjoyed reviewing and engaging with your project so far, and I'm looking forward to watching and interacting with you as you refine, fix, improve and extend it. You've lots to do, and I'm excited to see how this progresses.

Onwards... 🚀


tim tim osahenru ~ 05 Dec 2022 11:21 a.m.

Hello Nick,

Thank you for your detailed feedback

I'm making changes to the feedbacks I understand and to the ones I don't I'm pointing them out to you.

I have been able to update my README file by adding a Delevoper setup section and also updated my install requirements as shown below

Screenshot_57.png


tim tim osahenru ~ 05 Dec 2022 11:32 a.m.

The first error you encounter when running the application is a syntax error where omitted .url from my image src as shown below

Error is seen on line 63 bad.png

Fixed error good.png


tim tim osahenru ~ 05 Dec 2022 11:36 a.m. (updated: 05 Dec 2022 12:01 p.m.)

As for the floated form I notice the position of the form changes based on screen size I'm still figuring out to make in fixed on all screen size, frontend isn't my strong suit.

However, I have included a placeholder to all forms where necessary as shown below form.png


tim tim osahenru ~ 05 Dec 2022 7:16 p.m. (updated: 10 Dec 2022 4:05 p.m.)

NEW FUNCTIONALITIES

You can now make use of the search bar by making a search query with either the country, name of an engineer or a tech stack.

Also the number of Engineers on our platform can also be viewed from the homepage count.jpg

Added a create button also to the projects page create.jpg


ntoll Nicholas Tollervey ~ 06 Dec 2022 5:35 p.m.

Hi Tim,

It's great to see you making progress. Here's some more feedback. 👍

I'll give more feedback when you let me know the code is up on GitHub. 🚀


tim tim osahenru ~ 06 Dec 2022 6:22 p.m.

Hello Nick, thank you for keeping track of my project progress I have pushed the latest changes to the project repo on github

I have included pip install black to my README file and also gitinore all .pyc files and relative pycaches as for this line of code

    def get_queryset(self):
        public_projects = Project.objects.filter(make_public=True)
        return public_projects

not sure how to go about the feedback you gave, however, for me having the two lines of codes makes it readable for me and I assume any developer(newbies especially).


ntoll Nicholas Tollervey ~ 06 Dec 2022 7:22 p.m.

Hi Tim,

Let's imagine a metaphor: there is a student and a teacher. The student asks the teacher a question, "what is the capital city of Ghana?" and the teacher writes the answer on a slip of paper, puts it into an envelope labeled "the capital city of Ghana" and then hands it to the student. Surely the teacher could just reply with the answer..! 🇬🇭 😄

That's a little bit like your function. Calling it is like the student asking a question, and inside the function the teacher is wrapping the answer in the public_projects envelope.

Why not just return the result like this..?

def get_queryset(self):
    return Project.objects.filter(make_public=True)

Of course, you could say that public_projects is helpful because its name describes what it contains. But I'd argue that Project.objects.filter(make_public=True) is equally as revealing, so the extra "envelope" of public_projects isn't needed.

Clearly this is a question of taste and preference, and please don't think that you'll be penalised for doing it one way or another. But I just wanted to highlight that, in general, less code is better unless this makes the code harder to understand.

Does this help..?

Keep up the engagement, refinement and additions to the project. 🚀


tim tim osahenru ~ 07 Dec 2022 3:51 a.m.

...It helps alot Nick, thank you for the insight. Honestly your point became clearer with the illustration given I've however made this change

I have updated the README file with a step by step guide on how to setup a virtual environment on a python project

virtual.png

also added to the README file is a mini tutorial on how to use dev-locate as an Engineer and also a Hiring manager

tutorial.png


tim tim osahenru ~ 07 Dec 2022 5:06 a.m.

I'm currently working on two features(function and code related); a messaging functionality and a test script for image_url, when I am done I really will love to host this project on the suggested platform.

I am also rethinking my design choice, as you mentioned, rather than having a HiringManager model I'll just let every user sign up as an Engineer and then differentiate each object with a flag, though I'm not sure what that flag could be, thanks for these insights Nick.


ntoll Nicholas Tollervey ~ 07 Dec 2022 3:53 p.m.

Hi Tim,

It sounds like you're working on the updates to the project in the right sort of a way.

Just a few pointers to keep in mind:

A quick heads up. I think I'll be in a good position to provide your final assessment for the grade when I see the following things (note the new features requested):

I've mentioned the new requests now, because I didn't want to overwhelm you with stuff to do when I sent over my initial feedback. 👍

But given that you've responded to my points and progress is going well, I wanted to share with you those last things that will get this project over the line... and so you get the best possible mark that reflects your level of attainment while also giving you a portfolio project you can show to potential employers (who'll be able to see our conversation and evidence of the way you engage as a software engineer).

The assessment part of the grading process is described here and the assessment criteria mentors use to calculate the final mark is found here.

As always, don't hesitate to ask questions, and keep up the good work. 🚀


tim tim osahenru ~ 08 Dec 2022 8:51 p.m.

Hello Nick,

Thanks for the effort you've been putting to mentor me on this project it's been mind blowing, I have had to work with some new technology like Sphinx.

So, I have to put on hold the messaging feature I was working on to focus on the core assessments for this Grade

below is the code for the test I am trying to run, I am having a challenge trying to mock an image for my test.py

from django.test import TestCase
from .models import Project
from django.core.files.images import ImageFile
from io import BytesIO


class TestModels(TestCase):
    def setUp(self):
        new_image = BytesIO()
        self.image = Project.objects.create(
            image=ImageFile(new_image)
        )

    def test_image_url(self):

        self.assertEquals(self.image.image_url, '')

tim tim osahenru ~ 08 Dec 2022 9:07 p.m.

Below is a snapshot of my documentation using Sphinx

Screenshot_88.png

I really had FUN using it for the first time, a few question/observations

  1. Besides this style of documentation been available to the public what differentiates it from having a traditional README file?
  2. How do I write a one line code block using Sphinx like in a markdown I can do this using double backtick.
  3. I also tried to take my documentation journey a step further by trying to push my docs to readthedocs.org but, I keep getting this error when I try to build

Screenshot_86.png


tim tim osahenru ~ 08 Dec 2022 9:16 p.m.

Users can be able to extract their data in JSON format from their profile by clicking the extract data button as shown below

1.png

giving an output like this

2.png


tim tim osahenru ~ 08 Dec 2022 9:23 p.m.

As regards the users experience with this project I have 2 sets of individual testing out this project one an experienced developer and the other a novice, just to properly cover all areas I might have skipped while setting up my documentation, I will also love to deploy this project on the suggested platform but I guess I have to be done with these key pointers first.

Thanks Nick.


tim tim osahenru ~ 09 Dec 2022 5:22 p.m.

User experience While some users found it easy setting up their environment and cloning the project because of their relative programming experience, others found it quite difficult so, I had to include more details to the documentation on how to get the project up and running on a local machine.

User one

whatsapp.png as a result of this feedback I had to include some basic git syntax, initially assumed every user will be familiar with git.

Also added to the documentation a guide on how to get Python and Git running on your machine.

User two An experienced developer gave a more resourceful feedback on the project as shown below

received.png

and I also had to explain the decision behind my design choices as shown below

sent.png


tim tim osahenru ~ 10 Dec 2022 12:26 a.m.

Hello Nick, I'm trying to test the waters by deploying my project on pythonanywhere but I keep getting this error not sure why, it seems my index file is missing but it still runs perfectly on my local machine.

Screenshot_101.png


tim tim osahenru ~ 10 Dec 2022 4:51 a.m. (updated: 10 Dec 2022 4:53 a.m.)

Fixed!!!

So, apparently I was pointing to the wrong folder in the working directory

Screenshot_102.png

This web application is available for preview at

Hurray!!!


tim tim osahenru ~ 10 Dec 2022 2:48 p.m. (updated: 10 Dec 2022 4:11 p.m.)

Messaging feature

I'm adding a messaging feature to this project where authenticated users can send messages.

Go to a user's profile click message and drop a message for them, when the user logs in they can see the message and you too can also see the message sent to your inbox, the challenge I'm having is the receiver's inbox is still blank so when they log in they don't see any messages although the SENDER sees theirs in their inbox

below is my code

models.py

class Inbox(models.Model):
    engineer = models.ForeignKey(Engineer, null=True, on_delete=models.CASCADE, related_name='engineer')
    sender = models.ForeignKey(Engineer, null=True, on_delete=models.CASCADE, related_name='mail_sender')
    receiver = models.ManyToManyField(Engineer, related_name="mail_receiver")
    body = models.CharField(max_length=500)
    updated = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created"]

    def __str__(self):
        return self.sender.username

views.py

class CreateMessage(LoginRequiredMixin, CreateView):
    model = Inbox
    template_name = "../templates/create-message.html"
    fields = ["body"]

    def get_context_data(self, **kwargs):
        context = super(CreateMessage, self).get_context_data()
        pk = self.kwargs.get('pk')
        context['engineer'] = Engineer.objects.get(id=pk)
        context['mails'] = Inbox.objects.filter(sender=self.request.user)
        return context

    def form_valid(self, form):
        if form.is_valid():
            form.save(commit=False)
            form.instance.sender = self.request.user
            form.save()
            return redirect("create-message", pk=self.request.user.id)
        return super(CreateMessage, self).form_valid(form)

tim tim osahenru ~ 11 Dec 2022 5:48 p.m. (updated: 11 Dec 2022 5:55 p.m.)

Update on writing test

So, I got confused with the concept of writing a test for my image_url after much research and study I think I figured it out here.

class ProjectImageUploadTest(TestCase):

    def setUp(self):
        self.engineer = Engineer.objects.create(username='Engineer')
        self.project = {
                'engineer': self.engineer,
                'name': 'Test project',
                'tech_used': 'Test tech',
                'updated': timezone.now(),
                'created': timezone.now(),
                'image': None,
                'description': 'Test description',
                'repo_url': 'https://github.com/TimOsahenru/dev-locate',
                'live_url': 'http://timosahenru.pythonanywhere.com/',
                'make_public': False
            }
        self.obj = Project.objects.create(**self.project)

    def test_upload_image(self):

        self.assertEquals(self.obj.image_url, '')

Earlier I noticed I noticed when a user creates a project without any image it gave an error, two fix to this error as shown below

One, was to provide a default image for all projects image = models.ImageField(default="project.png") two, was to provide an object without an image by creating a function as shown below

 @property
    def image_url(self):
        try:
            url = self.image.url
        except:
            url = ""
        return url

    class Meta:
        ordering = ["-created"]

The test checks out if my second fix is valid and my test results agrees as shown below also

Screenshot_105.png


tim tim osahenru ~ 13 Dec 2022 4:43 p.m. (updated: 13 Dec 2022 4:45 p.m.)

Update Messaging function

When I try to establish a relationship between my Engineer models and the Inbox models I'm getting this error message not sure what to make of it, I think it says I can't establish the relationship like that but don't know why

Screenshot_108.png

Inbox models.py

class Inbox(models.Model):
    sender = models.ForeignKey(Engineer, null=True, on_delete=models.CASCADE, related_name='mail_sender')
    receiver = models.ForeignKey(Engineer, null=True, on_delete=models.CASCADE, related_name="mail_receiver")
    message = models.CharField(max_length=500)
    updated = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created"]

    def __str__(self):
        return self.sender.username

Engineer models.py

class Engineer(AbstractUser):
    username = models.CharField(max_length=200, null=True)
    email = models.EmailField(unique=True, null=True)
    bio = models.TextField()
    avatar = models.ImageField(default="profile.png")
    country = models.CharField(max_length=200)
    years_of_experience = models.PositiveIntegerField(null=True, blank=True)
    tech_stack = models.CharField(null=True, blank=True, max_length=300)
    inbox = models.ForeignKey(Inbox, null=True, on_delete=models.CASCADE)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username"]

    def __str__(self):
        return self.username

ntoll Nicholas Tollervey ~ 13 Dec 2022 5:42 p.m.

Hi Tim,

Apologies for my slow response time, I've had a cold but feeling much better now. Also, I continue to enjoy reading your updates about the project and appreciate all the work you've been putting in. It feels like it's really coming together.

You have asked several questions, and here are my answers:

I really appreciated you sharing your experiences with user feedback. I can't stress how important this is - especially for web development since we, as developers, inevitably make so many assumptions based on our own (rather than our user's) experiences. User feedback ensures we're solving their problems or helping them with their situations, rather than imagining something else based on our own limited experience. It's actually quite humbling yet really very useful. 👍

Seeing your work on PythonAnywhere put a huge smile on my face. This is an important step... not only is it important to create things with your code, you should also share them... and that in itself is often a technical journey of discovery (as you found with the issue about the working directory configuration). By the way, I think PythonAnywhere are a great example of a website / service that has really great documentation, and somewhere from which you can draw inspiration for your own docs.

I tried to have a go with the new messaging functionality, but alas, encountered a problem (see the error message below):

inbox_error.png

Without looking at the code, I'm not sure what's going on. But by the looks of it I don't think the expected mailbox is getting created in the database when the user is also created. Could you take a look and fix it please..?

Finally, great work on overcoming the issues with the test. To add some context about testing here, the CodeGrades website itself is written in Django and has several hundred tests. Things are configured in such a way that the website won't deploy/update unless all the tests pass. If I encounter a bug, I usually write a test to recreate the bug, and then fix it by making the test pass. In that way, I can be certain I never re-introduce problem behaviour into the code. This is perhaps the most useful thing tests bring to our code: the confidence to be able to make changes knowing that the test suite will fail if we mess up. 😄

It feels like the project is very close to a really good state. From my perspective I think you have three tasks ahead of you (all mentioned above):

Onwards. 🚀


tim tim osahenru ~ 15 Dec 2022 1:34 a.m.

Hello Nick, so sorry to hear about your health condition and I'm happy to hear you're much better and thanks for the review and shading light on the questions asked.

UPDATE ON MESSAGE FUNCTION So the reason for the error I initially highlighted while trying to create a message function was because I had two modules(mails and accounts) importing from each other, so a fix to that was to include the Inbox models in the account modules rather than creating a separate modules (mails).

I had to delete my mails modules and I ran into a couple of errors to resolve these errors I had to drop my database and then the accounts_engineer table wasn't showing, using python manage.py migrate --run-syncdb to migrate my changes helped resolved that issue.

Message model

class Message(models.Model):
    sender = models.ForeignKey("Engineer", null=True, on_delete=models.CASCADE, related_name='mail_sender')
    receiver = models.ForeignKey("Engineer", null=True, on_delete=models.CASCADE, related_name="mail_receiver")
    message = models.CharField(max_length=500)
    updated = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created"]

    def __str__(self):
        return self.sender.username

The Message model is pretty straight forward like every other model we have been creating


tim tim osahenru ~ 15 Dec 2022 2:10 a.m.

While creating the logic for the message function I ran into yet another error resulting from the line of this code context['messages'] = sender_messages.union(received_messages) error message is shown below

Screenshot_117.png

In Python the union() method returns a set that contains all items from the original set, and all items from the specified set

However, had to change approach noticing that the error above is peculiar to using the sqlite3 database as discovered from this stackoverflow answer.

.distinct() method can be used to perform this operation to achieve similar results. In Python .distinct() is an expression/query clause used in SQL to remove duplicated results and return unique results based on the column we select.

The above line of code was later replaced with context['messages'] = (sender_messages | received_messages).distinct() below is the full code to how the message function was created


tim tim osahenru ~ 15 Dec 2022 2:15 a.m. (updated: 15 Dec 2022 7:59 a.m.)

class CreateMessage(CreateView):
    model = Message
    template_name = '../templates/create-message.html'
    fields = ['message']

    def get_context_data(self, **kwargs):
        context = super(CreateMessage, self).get_context_data()
        context['engineers'] = Engineer.objects.all().exclude(username=self.request.user)
        pk = self.kwargs.get("pk")
        context['engineer'] = Engineer.objects.get(pk=pk)
        engineer = context['engineer']
        sender_messages = Message.objects.filter(sender=self.request.user, receiver=engineer)
        received_messages = Message.objects.filter(sender=engineer, receiver=self.request.user)
        context['messages'] = (sender_messages | received_messages).distinct()
        return context

    def form_valid(self, form):
        if form.is_valid():
            message = form.save(commit=False)
            message.sender = self.request.user
            pk = self.kwargs.get("pk")
            message.receiver = Engineer.objects.get(pk=pk)
            form.save()
            return redirect('message', pk=self.request.user.id)
        return super(CreateMessage, self).form_valid(form)

Brief explanation of the above code block


tim tim osahenru ~ 15 Dec 2022 7:51 a.m. (updated: 15 Dec 2022 7:57 a.m.)

context['engineers'] = Engineer.objects.all().exclude(username=self.request.user) We don't want a logged in user messaging themselves so we get all the Engineers on our platform excluding the logged in user

context['engineer'] = Engineer.objects.get(pk=pk) Get the id of the Engineer we want to message

sender_messages = Message.objects.filter(sender=self.request.user, receiver=engineer) Filter the Message objects where the requested user(logged in user) is the sender and the profile of the engineer clicked is the receiver all saved in sender_messages variable

received_messages = Message.objects.filter(sender=engineer, receiver=self.request.user) For an Engineer to receive the message they have to be logged in (now the requested user) while the profile of the engineer they clicked on the sender

With the help of the .distict() function we can pass both querysets (sender_messages and received_messages) in the variable messages

Processing a valid form

    def form_valid(self, form):
        if form.is_valid():
            message = form.save(commit=False)
            message.sender = self.request.user
            pk = self.kwargs.get("pk")
            message.receiver = Engineer.objects.get(pk=pk)
            form.save()
            return redirect('message', pk=self.request.user.id)
        return super(CreateMessage, self).form_valid(form)

While creating the message object we want to pass both the sender of the message and the receiver of the message before the message is created or saved, the sender is the requested user and then we get the id of the engineer receiving the message and once the message object has been created the user is redirected to their message inbox.


tim tim osahenru ~ 15 Dec 2022 7:40 p.m. (updated: 15 Dec 2022 7:53 p.m.)

I've had quite a struggle trying to deploy my documentation on readthedocs, but after several trials finally figured it out.

added a .readthedocs.yaml file to the root path of my docs folder to provide the python version in which the project was built.

Trying to push my code to github I ran into these git errors git_error.png

after spending sometime on stackoverflow I discovered it was a git error(don't know why yet) but with this line of codes

del .git\index
git reset

I was able to solve this error

For some reasons I ran into yet another error when I tried to .\make html my .rst file as shown below sphinx_error.png

I understand what the error says, although I don't know how the _static folder got missing but I am happy I understood the error message and could solve it by creating a _static folder inside the source folder as shown below sphinx_tree.png


tim tim osahenru ~ 15 Dec 2022 7:58 p.m.

Deploying on readthedocs

As I mentioned earlier I ran into several errors while trying to deploy my documentation, I had to include a .readthedocs.yaml and a requirements.txt to the root directory of the docs folder and why I tried to build I ran into this error several times

readthedocs_error.png

A solution to this was to exclude whatever dependency that flags an error

VIOLLA!!! complete_build.png

I was able to build my documentation on readthedocs


tim tim osahenru ~ 15 Dec 2022 8:04 p.m.

Message template Designed the messaging template as shown below message.png

the project is made available at and the documentation to this project is also available here

Have fun interacting with this project Phew!!! fingers crossed


ntoll Nicholas Tollervey ~ 17 Dec 2022 2:25 p.m.

Hi Tim,

I've taken a look around the code on GitHub, the website online, and read the docs you managed to deploy online (well done!). I think I have enough evidence and observed enough activity and growth to be able to write up the assessment for your grade. This is generally a three step process:

  1. I write up my assessment.
  2. A third party checks it, points out corrections or advises on ways I can improve my feedback to you.
  3. Once the review is approved in step 2, you get your results.

I expect you should receive them sometime at the beginning of next week. 🚀


tim tim osahenru ~ 18 Dec 2022 4:06 p.m.

Well noted. Fingers crossed


Back to top