Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Habit Tracking Feature #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified backend/backend/__pycache__/settings.cpython-310.pyc
Binary file not shown.
Binary file modified backend/backend/__pycache__/urls.cpython-310.pyc
Binary file not shown.
Binary file modified backend/backend/api/__pycache__/urls.cpython-310.pyc
Binary file not shown.
2 changes: 2 additions & 0 deletions backend/backend/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from rest_framework.routers import DefaultRouter
from journaling.api.urls import journal_router
from habits.api.urls import habit_router
from django.urls import path, include


router = DefaultRouter()
router.registry.extend(journal_router.registry)
router.registry.extend(habit_router.registry)

urlpatterns = [
path('', include(router.urls)),
Expand Down
14 changes: 12 additions & 2 deletions backend/backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,19 @@
"""

from pathlib import Path
import json

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# Config File for the entire app
config_file = BASE_DIR.parent / "config.json"

with config_file.open() as json_file:
config_data = json.load(json_file)
HOST_ADDRESS = config_data.get('HOST_ADDRESS', 'localhost')



# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
Expand All @@ -38,13 +47,14 @@
'rest_framework',
'corsheaders',
'journaling.apps.JournalingConfig',
'habits.apps.HabitsConfig',
]

CORS_ALLOWED_ORIGINS = [
'http://localhost:5173',
'http://localhost:8081',
'exp://192.168.254.26:8081',
'http://192.168.254.26:8081',
f'exp://{HOST_ADDRESS}:8081',
f'http://{HOST_ADDRESS}:8081',
]

MIDDLEWARE = [
Expand Down
3 changes: 2 additions & 1 deletion backend/backend/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@

urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('journaling.api.urls')),
path('api/', include('backend.api.urls')),
path('api/', include('journaling.api.urls'))
]
Binary file modified backend/db.sqlite3
Binary file not shown.
Empty file added backend/habits/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions backend/habits/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.contrib import admin
from .models import Habit

# Register your models here.
admin.site.register(Habit)
13 changes: 13 additions & 0 deletions backend/habits/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from rest_framework import serializers
from ..models import Habit

class HabitSerializer(serializers.ModelSerializer):
class Meta:
model = Habit
fields = ['id', 'name', 'description', 'good_habit', 'date_started', 'date_last_checked']


class HabitCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Habit
fields = ['name', 'description', 'good_habit']
10 changes: 10 additions & 0 deletions backend/habits/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import HabitView

habit_router = DefaultRouter()
habit_router.register(r'habit', HabitView)

urlpatterns = [
path('', include(habit_router.urls)),
]
18 changes: 18 additions & 0 deletions backend/habits/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from rest_framework import viewsets, status
from ..models import Habit
from .serializers import HabitSerializer, HabitCreateUpdateSerializer
from rest_framework.response import Response

# Create your views here.

class HabitView(viewsets.ModelViewSet):
queryset = Habit.objects.all()

def get_serializer_class(self):
if self.action in ['create', 'update', 'partial_update']:
return HabitCreateUpdateSerializer
else:
return HabitSerializer

def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
6 changes: 6 additions & 0 deletions backend/habits/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class HabitsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'habits'
25 changes: 25 additions & 0 deletions backend/habits/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 4.2.7 on 2023-12-13 18:21

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Habit',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=25)),
('description', models.CharField(max_length=125)),
('good_habit', models.BooleanField(default=True)),
('date_started', models.DateTimeField(auto_now_add=True)),
('date_last_checked', models.DateTimeField(auto_now=True)),
],
),
]
Empty file.
11 changes: 11 additions & 0 deletions backend/habits/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.db import models

# Create your models here.


class Habit(models.Model):
name = models.CharField(max_length = 25)
description = models.CharField(max_length = 125)
good_habit = models.BooleanField(default = True)
date_started = models.DateTimeField(auto_now_add=True)
date_last_checked = models.DateTimeField(auto_now=True)
4 changes: 4 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

{
"HOST_ADDRESS": "<YOUR_ADDRESS_HERE>"
}
7 changes: 5 additions & 2 deletions frontendMobile/src/Api/apiEndpoints.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// This is where all the API endpoints are handled (using a REST framework)
// I made sure to put them all in one file to make frontend dev and backend dev communication a lot easier!

DOMAIN = 'http://192.168.254.26:8000' // Put the server to your backend here!
HOST = "<YOUR_ADDRESS_HERE>" // Put the IP here!
DOMAIN = `http://${HOST}:8000/`

const endpoints = {
getJournal: `${DOMAIN}/api/journal`,
Expand All @@ -10,6 +11,8 @@ const endpoints = {
journal: `${DOMAIN}/api/create_journal/`,
journalPage: `${DOMAIN}/api/create_journal_page/`,

getHabit: `${DOMAIN}/api/habit/`,

postApiResponse(endpoint, data, handleOk = () => {}, handleError = () => {}) {
console.log("Sender's Data: ", data)

Expand Down Expand Up @@ -80,4 +83,4 @@ const endpoints = {

};

export default endpoints;
export default endpoints;
48 changes: 48 additions & 0 deletions frontendMobile/src/Components/Habits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState, useCallback } from 'react';
import { View, ScrollView, Text, Button, StyleSheet } from 'react-native';
import { mainStyle } from '../Styles/Styles';
import { useFocusEffect } from '@react-navigation/native'
import dateTimeFormat from '../Utils/DateTimeFormat';
import endpoints from '../Api/apiEndpoints';

const s = StyleSheet.create(mainStyle);

const Habits = ({sortBy, navigation}) => {
const getHabits = endpoints.getHabit;

const [habits, setHabits] = useState([]);
useFocusEffect(useCallback(() => {endpoints.getAPIResponse(getHabits, setHabits)}, []));

const sortedHabits = habits.slice().sort(sortBy);

const controlStreak = (goodHabit) => {
if (goodHabit) {
console.log("Habit is good!");
} else {
console.log("Habit is bad!");
}
}

return (
<ScrollView>
{sortedHabits.map((habit) => (
<View
key={habit.id}
style={s.journalView.container}
>
<Text style={s.journalView.type}>{habit.name}</Text>
<View style={s.horizontalLine} />
<Text>{habit.description}</Text>
<Text>Streak: (coming soon)</Text>
<Button
style={ habit.good_habit ? s.journal.button : s.buttonText }
title={ habit.good_habit ? 'Complete' : 'Relapse' }
onPress={() => controlStreak(habit.good_habit)}
/>
</View>
))}
</ScrollView>
)
}

export default Habits;
2 changes: 2 additions & 0 deletions frontendMobile/src/Navigators/HomeStackNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import JournalingScreen from '../Screens/Journaling/JournalingScreen';
import ViewJournalsScreen from '../Screens/Journaling/ViewJournalsScreen';
import ViewPagesScreen from '../Screens/Journaling/ViewPagesScreen';
import SaveJournalingScreen from '../Screens/Journaling/SaveJournalScreen';
import CreateHabit from '../Screens/Habits/CreateHabit';
import { createStackNavigator } from '@react-navigation/stack'

const Nav = createStackNavigator();
Expand All @@ -19,6 +20,7 @@ const HomeStackNavigator = ({navigation}) => {
<Nav.Screen name="Save Journal" component={SaveJournalingScreen} />
<Nav.Screen name="View Journals" component={ViewJournalsScreen} />
<Nav.Screen name="My Journal" component={ViewPagesScreen} />
<Nav.Screen name="Create Habit" component={CreateHabit} />
</Nav.Navigator>
)
}
Expand Down
66 changes: 66 additions & 0 deletions frontendMobile/src/Screens/Habits/CreateHabit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useState } from 'react';
import { View, ScrollView, Button, StyleSheet, TextInput, Text, TouchableOpacity } from 'react-native';
import { mainStyle } from '../../Styles/Styles';
import endpoints from '../../Api/apiEndpoints';

const s = StyleSheet.create(mainStyle);

const CreateHabit = ({navigation}) => {
const [error, setError] = useState('')
const [goodHabit, setGoodHabit] = useState(false)

const [habitData, setHabitData] = useState({
name: '',
description: '',
good_habit: true,
});

const handleChange = (name, value) => {
setHabitData({
...habitData,
[name]: value
});
};

const handlingPost = {
handleOk: () => {
setError("Successfully saved habit!");
navigation.navigate("Home", error);
},
handleError: () => {
setError("An error has occured while saving your habit!");
},
}
const handleSubmit = () => {
const habit = endpoints.getHabit;
endpoints.postApiResponse(habit, habitData, handlingPost.handleOk, handlingPost.handleError)
}

return (
<ScrollView>
<View style={s.container}>
<Text>{error}</Text>
<TextInput
placeholder="Name"
value={habitData.prompt}
onChangeText={(text) => handleChange('name', text)}
/>
<TextInput
placeholder="Description"
value={habitData.entry}
multiline
onChangeText={(text) => handleChange('description', text)}
/>
<Button
title={goodHabit ? 'Good' : 'Bad'}
onPress={() => setGoodHabit(!goodHabit)}
/>
<TouchableOpacity style={s.journal.submitButton} onPress={handleSubmit}>
<Text style={s.buttonText}>Submit</Text>
</TouchableOpacity>
</View>
</ScrollView>
)
}

export default CreateHabit;
27 changes: 25 additions & 2 deletions frontendMobile/src/Screens/HomeScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@ import React, { useState, useEffect } from 'react';
import { StyleSheet, Animated, Easing, Text, View, TouchableOpacity } from 'react-native';
import { mainStyle } from '../Styles/Styles';
import JournalingChoices from '../Components/JournalingChoices';
import Habits from '../Components/Habits';
import dateTimeFormat from '../Utils/DateTimeFormat';


const s = StyleSheet.create(mainStyle);

export default function HomeScreen({navigation}) {

const [currentTime, setCurrentTime] = useState(new Date());
useEffect(() => {
const intervalId = setInterval(() => {
setCurrentTime(new Date());
}, 1000);
return () => clearInterval(intervalId);
}, []);

const [journalChoicesVisible, setJournalChoicesVisible] = useState(false)
const openJournalChoices = () => setJournalChoicesVisible(true);
const closeJournalChoices = () => setJournalChoicesVisible(false);
Expand Down Expand Up @@ -35,6 +45,7 @@ export default function HomeScreen({navigation}) {
return (
<Animated.View style={[s.container, {opacity: fade}]}>
<Text style={s.text}>This is the Home Page!</Text>
<Text>{dateTimeFormat(currentTime)}</Text>
<View style={s.row}>
<TouchableOpacity style={s.button} onPress={openJournalChoices}>
<Text style={s.buttonText}>Start Journal</Text>
Expand All @@ -44,8 +55,20 @@ export default function HomeScreen({navigation}) {
</TouchableOpacity>
</View>
<View style={s.horizontalLine} />
<View testID='Habits'>
<Text>-- Habits Go Here --</Text>
<View style={s.myHabits.tab}>
<View style={s.row}>
<Text style={s.myHabits.text}>My Habits</Text>
</View>
<View style={s.row}>
<TouchableOpacity style={s.myHabits.addButton}
onPress={() => navigation.navigate("Create Habit")}
>
<Text style={s.myHabits.buttonText}>+</Text>
</TouchableOpacity>
</View>
</View>
<View style={s.column}>
<Habits sortBy={(a, b) => 0} />
</View>
<View style={s.horizontalLine} />
<JournalingChoices visible={journalChoicesVisible}
Expand Down
Loading