From 7c12f67d9f81b549238b10dcd74c47be9f3fd907 Mon Sep 17 00:00:00 2001 From: owengavi2 Date: Tue, 31 Mar 2026 14:41:56 -0400 Subject: [PATCH] implmented app --- car_spotting/app/database.sqlite | Bin 0 -> 135168 bytes car_spotting/app/migrations/0001_initial.py | 37 +++++++++++++++ car_spotting/app/migrations/__init__.py | 0 car_spotting/app/models.py | 16 +++++++ car_spotting/app/views.py | 47 ++++++++++++++++++++ planning.md | 4 +- 6 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 car_spotting/app/database.sqlite create mode 100644 car_spotting/app/migrations/0001_initial.py create mode 100644 car_spotting/app/migrations/__init__.py create mode 100644 car_spotting/app/models.py create mode 100644 car_spotting/app/views.py diff --git a/car_spotting/app/database.sqlite b/car_spotting/app/database.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..a8a6d4a7dcec2b4c641eee686c60891206976e86 GIT binary patch literal 135168 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 literal 0 HcmV?d00001 diff --git a/car_spotting/app/migrations/0001_initial.py b/car_spotting/app/migrations/0001_initial.py new file mode 100644 index 0000000..3dfc853 --- /dev/null +++ b/car_spotting/app/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 5.1.4 on 2026-03-31 16:15 + +import banjo.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="SpottingEvent", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("make", banjo.models.StringField(default="")), + ("model", banjo.models.StringField(default="")), + ("color", banjo.models.StringField(default="")), + ("bodystyle", banjo.models.StringField(default="")), + ("location", banjo.models.StringField(default="")), + ("instances", banjo.models.IntegerField(default=0)), + ("note", banjo.models.StringField(blank=True, default="", null=True)), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/car_spotting/app/migrations/__init__.py b/car_spotting/app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/car_spotting/app/models.py b/car_spotting/app/models.py new file mode 100644 index 0000000..3a4db47 --- /dev/null +++ b/car_spotting/app/models.py @@ -0,0 +1,16 @@ +from banjo.models import Model, StringField, IntegerField + + +class SpottingEvent(Model): + make = StringField() + model = StringField() + color = StringField() + bodystyle = StringField() + location = StringField() + instances = IntegerField() + note = StringField(blank=True, null=True) + + def __repr__(self): + rep = self.to_dict() + return rep + diff --git a/car_spotting/app/views.py b/car_spotting/app/views.py new file mode 100644 index 0000000..2581b00 --- /dev/null +++ b/car_spotting/app/views.py @@ -0,0 +1,47 @@ +from banjo.urls import route_get, route_post +from banjo.http import BadRequest, NotFound +from app.models import SpottingEvent + + +@route_get('all', args={}) +def list_events(params): + events = sorted(SpottingEvent.objects.all(), key=lambda event: event.make) + return {'events': [event.to_dict() for event in events]} + +@route_post('new', args={'make': str, 'model': str, 'color': str, 'bodystyle': str, 'location': str, 'note': str, 'instances': int} ) +def create_event(params): + event = SpottingEvent.from_dict(params) + event.save() + return {'event': event.to_dict()} + +@route_get('search', args={'category': str, 'value': str}) +def search_events(params): + validcat = ['id', 'make', 'model', 'color', 'bodystyle', 'location'] + if params['category'] not in validcat: + raise BadRequest('Invalid category: can search by id, make, model, bodystyle, or location') + if params['category'] == 'make': + matches = SpottingEvent.objects.filter(make=params['value']) + elif params['category'] == 'model': + matches = SpottingEvent.objects.filter(model=params['value']) + elif params['category'] == 'bodystyle': + matches = SpottingEvent.objects.filter(bodystyle=params['value']) + elif params['category'] == 'location': + matches = SpottingEvent.objects.filter(location=params['value']) + elif params['category'] == 'color': + matches = SpottingEvent.objects.filter(color=params['value']) + elif params['category'] == 'id': + matches = SpottingEvent.objects.filter(id=int(params['value'])) + if not matches: + raise NotFound("No Event Found (make and model start with capital letters, e.g. Honda Civic)") + return {'events': [event.to_dict() for event in matches]} + +@route_post('addnotes', args={'id': int, 'new_note': str}) +def add_note(params): + try: + event = SpottingEvent.objects.get(id=params['id']) + except: + raise NotFound('Event not found') + event.note = params['new_note'] + event.save() + return {'event': event.to_dict()} + diff --git a/planning.md b/planning.md index b19ea29..b8e55a4 100644 --- a/planning.md +++ b/planning.md @@ -7,13 +7,13 @@ My app is for people who like car spotting, I am my target user. Car spotting is ## 2. What need or problem will your app solve? How do you know this is really a need for your target user? How does the target user currently deal with the problem? -This app will act as a place to store cars I have spotted. It will include make, model, color, body style, city, and country that I saw it in. I will be able to search for a car that I saw by these characteristics. I also want to be able to add a note about the event. +This app will act as a place to store cars I have spotted. It will include make, model, color, body style, location that I saw it in, and amount of times I saw it. I will be able to search for a car that I saw by these characteristics. I also want to be able to add a note about the event. This app is a niche need, and it is for me. I thought of this after going to some carribbean islands and seeing new cars that I have never heard of before. I deal with this problem now by trying to rely on memory, which is not perfect. So, having a place to put these cars would be a great solution. ## 3. How will your app's design meet the need you have identified? -I will have methods to add these car spotting events coded into the app. I will support search by any of the qualities outlined earlier: make, model, color, body style, city, country. I will support adding notes about the spotting event. +I will have methods to add these car spotting events coded into the app. I will support search for car spotting events for: make, model, body style, color, and location. I will support adding notes about the spotting event. ## 4. How could your app possibly cause harm? (For example, could someone get hurt if data leaked or if someone used the app inappropriately?) What steps will you take to ensure that nobody is harmed by your app?