diff --git a/.gitignore b/.gitignore index bee997b..0f5277b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .env persist/ - +frontend2/ # Byte-compiled / optimized / DLL files __pycache__/ @@ -171,3 +171,34 @@ cython_debug/ .vscode/settings.json db .DS_Store + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dev-dist +dist-ssr +*.local +.env + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ \ No newline at end of file diff --git a/README.md b/README.md index 6af68fd..f6d1fd6 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,41 @@ # dont_forget -Self hosted shopping list - - - - -A listgroup (type of store) is made of up lists (store): Grocery Store - A category is a grouping of items are are associated to a list group - An item is associated to a list group and can customize the following values per list - stocked here - category - purchase count - note - - -ListGroup - id - name - -List - id - name - ListGroup(s) - categories - -Category - id - Name - ListGroup(s) - - -Item - id - Name - plural name - listGroup(s) - photo - quantity - category - Note - purchase_count - -ListItem - id - quantity - Item - List - active - completed + +Self hosted shopping list + +## Data Structure + +- A `ListGroup` (type of store), ex: Grocery, is made up of invidiual `List` objects (store), ex: Kroger +- A `Category` is a grouping of `Item` objects associated to a `ListGroup`, ex: Produce +- An `Item` is a object that is associated to a `Category`, ex: Bananas +- A `UnitOfMeasure` is used to determine the typical measurement for an `Item`, ex: Pound +- A `ListItem` is an item associated to a `ListGroup` and can customize the following values per `List` + - stocked here + - category + - purchase count + - note + +## API Endpoints + +- Model CRUD Endpoints + - ShoppingListAGroup + - ShoppingList + - Category + - UnitOfMeasure + - Item + #- ListItem + - ListCustomization + - Users +- Function Endpoints + + - Add item to list group + - Create list customization object it not present + - create ListItem for each list in shopping list group + - Mark item completed + + - update `completed` in `ListItem` object for each list in shopping list group + - increment purchase count in list customization object + + - Remove item from list group + - update `active` in `ListItem` object + +# https://github.com/suren-atoyan/react-pwa?tab=readme-ov-file diff --git a/backend/api/api.py b/backend/api/api.py index 0260b0e..26480f9 100644 --- a/backend/api/api.py +++ b/backend/api/api.py @@ -6,6 +6,7 @@ shopping_list_group_router, shopping_list_router, uom_router, + list_functions_router, user_auth_router, user_no_auth_router, list_customization_router, @@ -19,13 +20,15 @@ api = NinjaExtraAPI(title='Shopping List API', description='Endpoints for interacting with the shopping list application', urls_namespace="api") -api.add_router("/uom", uom_router, auth=JWTAuth(), tags=["Units of Measure"]) -api.add_router("/category", category_router, auth=JWTAuth(), tags=["Categories"]) + api.add_router("/shopping_list_group", shopping_list_group_router, auth=JWTAuth(), tags=["Shopping List Group"]) api.add_router("/shopping_list", shopping_list_router, auth=JWTAuth(), tags=["Shopping List"]) -api.add_router("/list_customization", list_customization_router, auth=JWTAuth(), tags=["List Customization"]) -api.add_router("/list_item", list_item_router, auth=JWTAuth(), tags=["List Item"]) +api.add_router("/category", category_router, auth=JWTAuth(), tags=["Categories"]) +api.add_router("/uom", uom_router, auth=JWTAuth(), tags=["Units of Measure"]) api.add_router("/item", item_router, auth=JWTAuth(), tags=["Items"]) +api.add_router('/list_functions', list_functions_router, auth=JWTAuth(), tags=['List Functions']) +#api.add_router("/list_item", list_item_router, auth=JWTAuth(), tags=["List Item"]) +api.add_router("/list_customization", list_customization_router, auth=JWTAuth(), tags=["List Customization"]) api.add_router("/users", user_no_auth_router, tags=["Users"]) api.add_router("/users", user_auth_router, auth=JWTAuth(), tags=["Users"]) diff --git a/backend/api/migrations/0001_initial.py b/backend/api/migrations/0001_initial.py index 617e115..c5e9bca 100644 --- a/backend/api/migrations/0001_initial.py +++ b/backend/api/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0.7 on 2024-08-06 22:17 +# Generated by Django 5.0.7 on 2024-09-12 01:40 import django.db.models.deletion import django.utils.timezone @@ -22,43 +22,184 @@ class Migration(migrations.Migration): fields=[ ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ( + 'is_superuser', + models.BooleanField( + default=False, + help_text='Designates that this user has all permissions without explicitly assigning them.', + verbose_name='superuser status', + ), + ), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('first_name', models.CharField(max_length=255)), ('last_name', models.CharField(max_length=255)), ('email', models.EmailField(max_length=254, unique=True)), - ('is_active', models.BooleanField(default=True)), ('created_on', models.DateTimeField(default=django.utils.timezone.now, editable=False)), ('updated_on', models.DateTimeField(null=True)), ('is_staff', models.BooleanField(default=False)), ('api_token', models.UUIDField(default=uuid.uuid4, editable=False)), ('token_created_date', models.DateTimeField(default=django.utils.timezone.now)), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ( + 'groups', + models.ManyToManyField( + blank=True, + help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', + related_name='user_set', + related_query_name='user', + to='auth.group', + verbose_name='groups', + ), + ), + ( + 'user_permissions', + models.ManyToManyField( + blank=True, + help_text='Specific permissions for this user.', + related_name='user_set', + related_query_name='user', + to='auth.permission', + verbose_name='user permissions', + ), + ), ], options={ 'db_table': 'users', 'ordering': ('created_on',), }, ), + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField()), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True, null=True)), + ( + 'created_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='category_created_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='category_updated_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + 'verbose_name': 'Category', + 'verbose_name_plural': 'Categories', + }, + ), + migrations.CreateModel( + name='HistoricalShoppingListGroup', + fields=[ + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ('name', models.CharField(db_index=True)), + ('created_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ( + 'history_type', + models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1), + ), + ( + 'created_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'history_user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + 'verbose_name': 'historical Shopping List Group', + 'verbose_name_plural': 'historical Shopping List Groups', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), migrations.CreateModel( name='HistoricalUnitOfMeasure', fields=[ ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), ('name', models.CharField(db_index=True)), ('plural_name', models.CharField(blank=True, null=True)), - ('created_at', models.DateTimeField()), - ('updated_at', models.DateTimeField()), - ('deleted_at', models.DateTimeField(blank=True, null=True)), - ('is_active', models.BooleanField(default=True)), - ('is_deleted', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), ('history_id', models.AutoField(primary_key=True, serialize=False)), ('history_date', models.DateTimeField(db_index=True)), ('history_change_reason', models.CharField(max_length=100, null=True)), - ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), - ('created_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), - ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), - ('updated_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), + ( + 'history_type', + models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1), + ), + ( + 'created_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'history_user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ 'verbose_name': 'historical Units of Measure', @@ -73,12 +214,18 @@ class Migration(migrations.Migration): fields=[ ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ( + 'is_superuser', + models.BooleanField( + default=False, + help_text='Designates that this user has all permissions without explicitly assigning them.', + verbose_name='superuser status', + ), + ), ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), ('first_name', models.CharField(max_length=255)), ('last_name', models.CharField(max_length=255)), ('email', models.EmailField(db_index=True, max_length=254)), - ('is_active', models.BooleanField(default=True)), ('created_on', models.DateTimeField(default=django.utils.timezone.now, editable=False)), ('updated_on', models.DateTimeField(null=True)), ('is_staff', models.BooleanField(default=False)), @@ -87,8 +234,19 @@ class Migration(migrations.Migration): ('history_id', models.AutoField(primary_key=True, serialize=False)), ('history_date', models.DateTimeField(db_index=True)), ('history_change_reason', models.CharField(max_length=100, null=True)), - ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), - ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ( + 'history_type', + models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1), + ), + ( + 'history_user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ 'verbose_name': 'historical user', @@ -98,26 +256,663 @@ class Migration(migrations.Migration): }, bases=(simple_history.models.HistoricalChanges, models.Model), ), + migrations.CreateModel( + name='Item', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(unique=True)), + ('plural_name', models.CharField()), + ('photo', models.ImageField(blank=True, null=True, upload_to='images/')), + ('default_quantity', models.IntegerField(default=1, verbose_name='Default Quantity')), + ('notes', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True, null=True)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.category')), + ( + 'created_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='item_created_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='item_updated_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + 'verbose_name': 'Item', + }, + ), + migrations.CreateModel( + name='ShoppingListGroup', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True, null=True)), + ( + 'created_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='slg_created_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='slg_updated_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + 'verbose_name': 'Shopping List Group', + }, + ), + migrations.CreateModel( + name='ShoppingList', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True, null=True)), + ( + 'created_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='sl_created_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='sl_updated_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ('list_group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.shoppinglistgroup')), + ], + options={ + 'verbose_name': 'Shopping List', + }, + ), + migrations.CreateModel( + name='ListItem', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('quantity', models.IntegerField(default=0)), + ('active', models.BooleanField(verbose_name='Active')), + ('completed', models.BooleanField(verbose_name='Completed')), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True, null=True)), + ( + 'created_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='li_created_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.item')), + ( + 'updated_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='li_updated_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'shopping_list_group', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.shoppinglistgroup'), + ), + ], + options={ + 'verbose_name': 'List Item', + }, + ), + migrations.AddField( + model_name='item', + name='list_group', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.shoppinglistgroup'), + ), + migrations.CreateModel( + name='HistoricalShoppingList', + fields=[ + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ('name', models.CharField(db_index=True)), + ('created_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ( + 'history_type', + models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1), + ), + ( + 'created_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'history_user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'list_group', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.shoppinglistgroup', + ), + ), + ], + options={ + 'verbose_name': 'historical Shopping List', + 'verbose_name_plural': 'historical Shopping Lists', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='HistoricalListItem', + fields=[ + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ('quantity', models.IntegerField(default=0)), + ('active', models.BooleanField(verbose_name='Active')), + ('completed', models.BooleanField(verbose_name='Completed')), + ('created_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ( + 'history_type', + models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1), + ), + ( + 'created_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'history_user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'item', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.item', + ), + ), + ( + 'shopping_list_group', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.shoppinglistgroup', + ), + ), + ], + options={ + 'verbose_name': 'historical List Item', + 'verbose_name_plural': 'historical List Items', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='HistoricalCategory', + fields=[ + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ('name', models.CharField()), + ('created_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ( + 'history_type', + models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1), + ), + ( + 'created_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'history_user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'shopping_list_group', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.shoppinglistgroup', + ), + ), + ], + options={ + 'verbose_name': 'historical Category', + 'verbose_name_plural': 'historical Categories', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.AddField( + model_name='category', + name='shopping_list_group', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.shoppinglistgroup'), + ), migrations.CreateModel( name='UnitOfMeasure', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('name', models.CharField(unique=True)), ('plural_name', models.CharField(blank=True, null=True)), - ('created_at', models.DateTimeField()), - ('updated_at', models.DateTimeField()), - ('deleted_at', models.DateTimeField(blank=True, null=True)), - ('is_active', models.BooleanField(default=True)), - ('is_deleted', models.BooleanField(default=False)), - ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uom_created_by', to=settings.AUTH_USER_MODEL)), - ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uom_updated_by', to=settings.AUTH_USER_MODEL)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True, null=True)), + ( + 'created_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='uom_created_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='uom_updated_by', + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ 'verbose_name': 'Units of Measure', }, ), + migrations.CreateModel( + name='ListCustomization', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('stocked', models.BooleanField(verbose_name='Stocked Here')), + ('Default Quantity', models.IntegerField(default=1)), + ('Purchase Count', models.IntegerField(default=0)), + ('notes', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(auto_now=True, null=True)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.category')), + ( + 'created_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='lc_created_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.item')), + ( + 'updated_by', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='lc_updated_by', + to=settings.AUTH_USER_MODEL, + ), + ), + ('shopping_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.shoppinglist')), + ( + 'default_unit_of_measure', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.unitofmeasure'), + ), + ], + options={ + 'verbose_name': 'List Customization', + }, + ), + migrations.AddField( + model_name='item', + name='unit_of_measure', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.unitofmeasure'), + ), + migrations.CreateModel( + name='HistoricalListCustomization', + fields=[ + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ('stocked', models.BooleanField(verbose_name='Stocked Here')), + ('Default Quantity', models.IntegerField(default=1)), + ('Purchase Count', models.IntegerField(default=0)), + ('notes', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ( + 'history_type', + models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1), + ), + ( + 'category', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.category', + ), + ), + ( + 'created_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'history_user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'item', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.item', + ), + ), + ( + 'shopping_list', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.shoppinglist', + ), + ), + ( + 'default_unit_of_measure', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.unitofmeasure', + ), + ), + ], + options={ + 'verbose_name': 'historical List Customization', + 'verbose_name_plural': 'historical List Customizations', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='HistoricalItem', + fields=[ + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ('name', models.CharField(db_index=True)), + ('plural_name', models.CharField()), + ('photo', models.TextField(blank=True, max_length=100, null=True)), + ('default_quantity', models.IntegerField(default=1, verbose_name='Default Quantity')), + ('notes', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ( + 'history_type', + models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1), + ), + ( + 'category', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.category', + ), + ), + ( + 'created_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'history_user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'updated_by', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), + ( + 'list_group', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.shoppinglistgroup', + ), + ), + ( + 'unit_of_measure', + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name='+', + to='api.unitofmeasure', + ), + ), + ], + options={ + 'verbose_name': 'historical Item', + 'verbose_name_plural': 'historical Items', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.AddConstraint( + model_name='shoppinglistgroup', + constraint=models.UniqueConstraint(fields=('name',), name='shopping-list-group-name'), + ), + migrations.AddConstraint( + model_name='shoppinglist', + constraint=models.UniqueConstraint(fields=('name',), name='shopping-list-name'), + ), + migrations.AddConstraint( + model_name='listitem', + constraint=models.UniqueConstraint(fields=('shopping_list_group', 'item'), name='item-shopping-list'), + ), + migrations.AddConstraint( + model_name='category', + constraint=models.UniqueConstraint(fields=('name',), name='category-name'), + ), migrations.AddConstraint( model_name='unitofmeasure', constraint=models.UniqueConstraint(fields=('name',), name='name'), ), + migrations.AddConstraint( + model_name='listcustomization', + constraint=models.UniqueConstraint(fields=('shopping_list', 'item'), name='item--shopping-list'), + ), + migrations.AddConstraint( + model_name='item', + constraint=models.UniqueConstraint(fields=('name', 'list_group'), name='item-list-group-name'), + ), ] diff --git a/backend/api/migrations/0002_alter_historicallistcustomization_stocked_and_more.py b/backend/api/migrations/0002_alter_historicallistcustomization_stocked_and_more.py new file mode 100644 index 0000000..59322f6 --- /dev/null +++ b/backend/api/migrations/0002_alter_historicallistcustomization_stocked_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.7 on 2024-09-17 00:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='historicallistcustomization', + name='stocked', + field=models.BooleanField(default=True, verbose_name='Stocked Here'), + ), + migrations.AlterField( + model_name='listcustomization', + name='stocked', + field=models.BooleanField(default=True, verbose_name='Stocked Here'), + ), + ] diff --git a/backend/api/migrations/0002_alter_historicalunitofmeasure_created_at_and_more.py b/backend/api/migrations/0002_alter_historicalunitofmeasure_created_at_and_more.py deleted file mode 100644 index e1078d1..0000000 --- a/backend/api/migrations/0002_alter_historicalunitofmeasure_created_at_and_more.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 5.0.7 on 2024-08-06 22:29 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='historicalunitofmeasure', - name='created_at', - field=models.DateTimeField(blank=True), - ), - migrations.AlterField( - model_name='historicalunitofmeasure', - name='updated_at', - field=models.DateTimeField(blank=True), - ), - migrations.AlterField( - model_name='unitofmeasure', - name='created_at', - field=models.DateTimeField(blank=True), - ), - migrations.AlterField( - model_name='unitofmeasure', - name='updated_at', - field=models.DateTimeField(blank=True), - ), - ] diff --git a/backend/api/migrations/0003_alter_historicallistcustomization_default_quantity_and_more.py b/backend/api/migrations/0003_alter_historicallistcustomization_default_quantity_and_more.py new file mode 100644 index 0000000..ccf3e1d --- /dev/null +++ b/backend/api/migrations/0003_alter_historicallistcustomization_default_quantity_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.0.7 on 2024-09-17 00:44 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0002_alter_historicallistcustomization_stocked_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='historicallistcustomization', + name='Default Quantity', + field=models.IntegerField(blank=True, default=1, null=True), + ), + migrations.AlterField( + model_name='listcustomization', + name='Default Quantity', + field=models.IntegerField(blank=True, default=1, null=True), + ), + migrations.AlterField( + model_name='listcustomization', + name='category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.category'), + ), + migrations.AlterField( + model_name='listcustomization', + name='default_unit_of_measure', + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.unitofmeasure' + ), + ), + ] diff --git a/backend/api/migrations/0003_alter_historicalunitofmeasure_created_at_and_more.py b/backend/api/migrations/0003_alter_historicalunitofmeasure_created_at_and_more.py deleted file mode 100644 index 2ac3390..0000000 --- a/backend/api/migrations/0003_alter_historicalunitofmeasure_created_at_and_more.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 5.0.7 on 2024-08-06 22:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0002_alter_historicalunitofmeasure_created_at_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='historicalunitofmeasure', - name='created_at', - field=models.DateTimeField(blank=True, null=True), - ), - migrations.AlterField( - model_name='historicalunitofmeasure', - name='updated_at', - field=models.DateTimeField(blank=True, null=True), - ), - migrations.AlterField( - model_name='unitofmeasure', - name='created_at', - field=models.DateTimeField(blank=True, null=True), - ), - migrations.AlterField( - model_name='unitofmeasure', - name='updated_at', - field=models.DateTimeField(blank=True, null=True), - ), - ] diff --git a/backend/api/migrations/0004_alter_historicalunitofmeasure_created_at_and_more.py b/backend/api/migrations/0004_alter_historicalunitofmeasure_created_at_and_more.py deleted file mode 100644 index fe24a1a..0000000 --- a/backend/api/migrations/0004_alter_historicalunitofmeasure_created_at_and_more.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 5.0.7 on 2024-08-07 21:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0003_alter_historicalunitofmeasure_created_at_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='historicalunitofmeasure', - name='created_at', - field=models.DateTimeField(blank=True, editable=False, null=True), - ), - migrations.AlterField( - model_name='historicalunitofmeasure', - name='updated_at', - field=models.DateTimeField(blank=True, editable=False, null=True), - ), - migrations.AlterField( - model_name='unitofmeasure', - name='created_at', - field=models.DateTimeField(auto_now_add=True, null=True), - ), - migrations.AlterField( - model_name='unitofmeasure', - name='updated_at', - field=models.DateTimeField(auto_now=True, null=True), - ), - ] diff --git a/backend/api/migrations/0005_category_historicalcategory_category_category_name.py b/backend/api/migrations/0005_category_historicalcategory_category_category_name.py deleted file mode 100644 index a01165c..0000000 --- a/backend/api/migrations/0005_category_historicalcategory_category_category_name.py +++ /dev/null @@ -1,113 +0,0 @@ -# Generated by Django 5.0.7 on 2024-08-17 18:28 - -import django.db.models.deletion -import simple_history.models -import uuid -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("api", "0004_alter_historicalunitofmeasure_created_at_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="Category", - fields=[ - ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ("name", models.CharField()), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=True, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ( - "created_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="category_created_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "updated_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="category_updated_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "Categorie", - }, - ), - migrations.CreateModel( - name="HistoricalCategory", - fields=[ - ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), - ("name", models.CharField()), - ("created_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("updated_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1), - ), - ( - "created_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "updated_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical Categorie", - "verbose_name_plural": "historical Categories", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.AddConstraint( - model_name="category", - constraint=models.UniqueConstraint(fields=("name",), name="category-name"), - ), - ] diff --git a/backend/api/migrations/0006_alter_category_options_and_more.py b/backend/api/migrations/0006_alter_category_options_and_more.py deleted file mode 100644 index ddd7e54..0000000 --- a/backend/api/migrations/0006_alter_category_options_and_more.py +++ /dev/null @@ -1,127 +0,0 @@ -# Generated by Django 5.0.7 on 2024-08-18 21:46 - -import django.db.models.deletion -import simple_history.models -import uuid -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("api", "0005_category_historicalcategory_category_category_name"), - ] - - operations = [ - migrations.AlterModelOptions( - name="category", - options={"verbose_name": "Categories"}, - ), - migrations.AlterModelOptions( - name="historicalcategory", - options={ - "get_latest_by": ("history_date", "history_id"), - "ordering": ("-history_date", "-history_id"), - "verbose_name": "historical Categories", - "verbose_name_plural": "historical Categoriess", - }, - ), - migrations.CreateModel( - name="HistoricalShoppingListGroup", - fields=[ - ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), - ("name", models.CharField(db_index=True)), - ("created_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("updated_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1), - ), - ( - "created_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "updated_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical Shopping List Group", - "verbose_name_plural": "historical Shopping List Groups", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="ShoppingListGroup", - fields=[ - ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ("name", models.CharField(unique=True)), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=True, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ("categories", models.ManyToManyField(to="api.category")), - ( - "created_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="slg_created_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "updated_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="slg_updated_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "Shopping List Group", - }, - ), - migrations.AddConstraint( - model_name="shoppinglistgroup", - constraint=models.UniqueConstraint(fields=("name",), name="shopping-list-group-name"), - ), - ] diff --git a/backend/api/migrations/0007_historicalitem_historicalshoppinglist_item_and_more.py b/backend/api/migrations/0007_historicalitem_historicalshoppinglist_item_and_more.py deleted file mode 100644 index 6845015..0000000 --- a/backend/api/migrations/0007_historicalitem_historicalshoppinglist_item_and_more.py +++ /dev/null @@ -1,265 +0,0 @@ -# Generated by Django 5.0.7 on 2024-08-22 21:12 - -import django.db.models.deletion -import simple_history.models -import uuid -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("api", "0006_alter_category_options_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="HistoricalItem", - fields=[ - ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), - ("name", models.CharField(db_index=True)), - ("plural_name", models.CharField()), - ("photo", models.TextField(blank=True, max_length=100, null=True)), - ("default_quantity", models.IntegerField(default=1, verbose_name="Default Quantity")), - ("notes", models.TextField(blank=True, null=True)), - ("created_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("updated_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1), - ), - ( - "category", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.category", - ), - ), - ( - "created_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "list_group", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.shoppinglistgroup", - ), - ), - ( - "unit_of_measure", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.unitofmeasure", - ), - ), - ( - "updated_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical Item", - "verbose_name_plural": "historical Items", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="HistoricalShoppingList", - fields=[ - ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), - ("name", models.CharField(db_index=True)), - ("created_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("updated_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1), - ), - ( - "created_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "list_group", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.shoppinglistgroup", - ), - ), - ( - "updated_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical Shopping List", - "verbose_name_plural": "historical Shopping Lists", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="Item", - fields=[ - ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ("name", models.CharField(unique=True)), - ("plural_name", models.CharField()), - ("photo", models.ImageField(blank=True, null=True, upload_to="images/")), - ("default_quantity", models.IntegerField(default=1, verbose_name="Default Quantity")), - ("notes", models.TextField(blank=True, null=True)), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=True, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ("category", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.category")), - ( - "created_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="item_created_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ("list_group", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.shoppinglistgroup")), - ("unit_of_measure", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.unitofmeasure")), - ( - "updated_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="item_updated_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "Item", - }, - ), - migrations.CreateModel( - name="ShoppingList", - fields=[ - ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ("name", models.CharField(unique=True)), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=True, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ( - "created_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="sl_created_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ("list_group", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.shoppinglistgroup")), - ( - "updated_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="sl_updated_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "Shopping List", - }, - ), - migrations.AddConstraint( - model_name="item", - constraint=models.UniqueConstraint(fields=("name", "list_group"), name="item-list-group-name"), - ), - migrations.AddConstraint( - model_name="shoppinglist", - constraint=models.UniqueConstraint(fields=("name",), name="shopping-list-name"), - ), - ] diff --git a/backend/api/migrations/0008_historicallistcustomization_listcustomization_and_more.py b/backend/api/migrations/0008_historicallistcustomization_listcustomization_and_more.py deleted file mode 100644 index 75ebd51..0000000 --- a/backend/api/migrations/0008_historicallistcustomization_listcustomization_and_more.py +++ /dev/null @@ -1,170 +0,0 @@ -# Generated by Django 5.0.7 on 2024-08-22 21:28 - -import django.db.models.deletion -import simple_history.models -import uuid -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("api", "0007_historicalitem_historicalshoppinglist_item_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="HistoricalListCustomization", - fields=[ - ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), - ("stocked", models.BooleanField(verbose_name="Stocked Here")), - ("Default Quantity", models.IntegerField(default=1)), - ("Purchase Count", models.IntegerField(default=0)), - ("notes", models.TextField(blank=True, null=True)), - ("created_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("updated_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1), - ), - ( - "category", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.category", - ), - ), - ( - "created_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "default_unit_of_measure", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.unitofmeasure", - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "item", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.item", - ), - ), - ( - "shopping_list", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.shoppinglist", - ), - ), - ( - "updated_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical Item", - "verbose_name_plural": "historical Items", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="ListCustomization", - fields=[ - ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ("stocked", models.BooleanField(verbose_name="Stocked Here")), - ("Default Quantity", models.IntegerField(default=1)), - ("Purchase Count", models.IntegerField(default=0)), - ("notes", models.TextField(blank=True, null=True)), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=True, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ("category", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.category")), - ( - "created_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="lc_created_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "default_unit_of_measure", - models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.unitofmeasure"), - ), - ("item", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.item")), - ("shopping_list", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.shoppinglist")), - ( - "updated_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="lc_updated_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "Item", - }, - ), - migrations.AddConstraint( - model_name="listcustomization", - constraint=models.UniqueConstraint(fields=("shopping_list", "item"), name="item--shopping-list"), - ), - ] diff --git a/backend/api/migrations/0009_historicallistitem_listitem_and_more.py b/backend/api/migrations/0009_historicallistitem_listitem_and_more.py deleted file mode 100644 index 325eeae..0000000 --- a/backend/api/migrations/0009_historicallistitem_listitem_and_more.py +++ /dev/null @@ -1,153 +0,0 @@ -# Generated by Django 5.0.7 on 2024-08-25 20:03 - -import django.db.models.deletion -import simple_history.models -import uuid -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("api", "0008_historicallistcustomization_listcustomization_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="HistoricalListItem", - fields=[ - ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), - ("Purchase Count", models.IntegerField(default=0)), - ("active", models.BooleanField(verbose_name="Active")), - ("completed", models.BooleanField(verbose_name="Completed")), - ("created_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("updated_at", models.DateTimeField(blank=True, editable=False, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1), - ), - ( - "created_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "item", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.item", - ), - ), - ( - "shopping_list", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.shoppinglist", - ), - ), - ( - "unit_of_measure", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="api.unitofmeasure", - ), - ), - ( - "updated_by", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical Item", - "verbose_name_plural": "historical Items", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="ListItem", - fields=[ - ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ("Purchase Count", models.IntegerField(default=0)), - ("active", models.BooleanField(verbose_name="Active")), - ("completed", models.BooleanField(verbose_name="Completed")), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=True, null=True)), - ("deleted_at", models.DateTimeField(blank=True, null=True)), - ("is_active", models.BooleanField(default=True)), - ("is_deleted", models.BooleanField(default=False)), - ( - "created_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="li_created_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ("item", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.item")), - ("shopping_list", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.shoppinglist")), - ("unit_of_measure", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.unitofmeasure")), - ( - "updated_by", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="li_updated_by", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "Item", - }, - ), - migrations.AddConstraint( - model_name="listitem", - constraint=models.UniqueConstraint(fields=("shopping_list", "item"), name="item-shopping-list"), - ), - ] diff --git a/backend/api/migrations/0010_alter_category_options_and_more.py b/backend/api/migrations/0010_alter_category_options_and_more.py deleted file mode 100644 index e6a72eb..0000000 --- a/backend/api/migrations/0010_alter_category_options_and_more.py +++ /dev/null @@ -1,52 +0,0 @@ -# Generated by Django 5.0.7 on 2024-08-26 15:34 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("api", "0009_historicallistitem_listitem_and_more"), - ] - - operations = [ - migrations.AlterModelOptions( - name="category", - options={"verbose_name": "Category", "verbose_name_plural": "Categories"}, - ), - migrations.AlterModelOptions( - name="historicalcategory", - options={ - "get_latest_by": ("history_date", "history_id"), - "ordering": ("-history_date", "-history_id"), - "verbose_name": "historical Category", - "verbose_name_plural": "historical Categories", - }, - ), - migrations.AlterModelOptions( - name="historicallistcustomization", - options={ - "get_latest_by": ("history_date", "history_id"), - "ordering": ("-history_date", "-history_id"), - "verbose_name": "historical List Customization", - "verbose_name_plural": "historical List Customizations", - }, - ), - migrations.AlterModelOptions( - name="historicallistitem", - options={ - "get_latest_by": ("history_date", "history_id"), - "ordering": ("-history_date", "-history_id"), - "verbose_name": "historical List Item", - "verbose_name_plural": "historical List Items", - }, - ), - migrations.AlterModelOptions( - name="listcustomization", - options={"verbose_name": "List Customization"}, - ), - migrations.AlterModelOptions( - name="listitem", - options={"verbose_name": "List Item"}, - ), - ] diff --git a/backend/api/migrations/0011_remove_category_deleted_at_remove_category_is_active_and_more.py b/backend/api/migrations/0011_remove_category_deleted_at_remove_category_is_active_and_more.py deleted file mode 100644 index bace9fb..0000000 --- a/backend/api/migrations/0011_remove_category_deleted_at_remove_category_is_active_and_more.py +++ /dev/null @@ -1,189 +0,0 @@ -# Generated by Django 5.0.7 on 2024-09-02 21:42 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0010_alter_category_options_and_more'), - ] - - operations = [ - migrations.RemoveField( - model_name='category', - name='deleted_at', - ), - migrations.RemoveField( - model_name='category', - name='is_active', - ), - migrations.RemoveField( - model_name='category', - name='is_deleted', - ), - migrations.RemoveField( - model_name='historicalcategory', - name='deleted_at', - ), - migrations.RemoveField( - model_name='historicalcategory', - name='is_active', - ), - migrations.RemoveField( - model_name='historicalcategory', - name='is_deleted', - ), - migrations.RemoveField( - model_name='historicalitem', - name='deleted_at', - ), - migrations.RemoveField( - model_name='historicalitem', - name='is_active', - ), - migrations.RemoveField( - model_name='historicalitem', - name='is_deleted', - ), - migrations.RemoveField( - model_name='historicallistcustomization', - name='deleted_at', - ), - migrations.RemoveField( - model_name='historicallistcustomization', - name='is_active', - ), - migrations.RemoveField( - model_name='historicallistcustomization', - name='is_deleted', - ), - migrations.RemoveField( - model_name='historicallistitem', - name='deleted_at', - ), - migrations.RemoveField( - model_name='historicallistitem', - name='is_active', - ), - migrations.RemoveField( - model_name='historicallistitem', - name='is_deleted', - ), - migrations.RemoveField( - model_name='historicalshoppinglist', - name='deleted_at', - ), - migrations.RemoveField( - model_name='historicalshoppinglist', - name='is_active', - ), - migrations.RemoveField( - model_name='historicalshoppinglist', - name='is_deleted', - ), - migrations.RemoveField( - model_name='historicalshoppinglistgroup', - name='deleted_at', - ), - migrations.RemoveField( - model_name='historicalshoppinglistgroup', - name='is_active', - ), - migrations.RemoveField( - model_name='historicalshoppinglistgroup', - name='is_deleted', - ), - migrations.RemoveField( - model_name='historicalunitofmeasure', - name='deleted_at', - ), - migrations.RemoveField( - model_name='historicalunitofmeasure', - name='is_active', - ), - migrations.RemoveField( - model_name='historicalunitofmeasure', - name='is_deleted', - ), - migrations.RemoveField( - model_name='historicaluser', - name='is_active', - ), - migrations.RemoveField( - model_name='item', - name='deleted_at', - ), - migrations.RemoveField( - model_name='item', - name='is_active', - ), - migrations.RemoveField( - model_name='item', - name='is_deleted', - ), - migrations.RemoveField( - model_name='listcustomization', - name='deleted_at', - ), - migrations.RemoveField( - model_name='listcustomization', - name='is_active', - ), - migrations.RemoveField( - model_name='listcustomization', - name='is_deleted', - ), - migrations.RemoveField( - model_name='listitem', - name='deleted_at', - ), - migrations.RemoveField( - model_name='listitem', - name='is_active', - ), - migrations.RemoveField( - model_name='listitem', - name='is_deleted', - ), - migrations.RemoveField( - model_name='shoppinglist', - name='deleted_at', - ), - migrations.RemoveField( - model_name='shoppinglist', - name='is_active', - ), - migrations.RemoveField( - model_name='shoppinglist', - name='is_deleted', - ), - migrations.RemoveField( - model_name='shoppinglistgroup', - name='deleted_at', - ), - migrations.RemoveField( - model_name='shoppinglistgroup', - name='is_active', - ), - migrations.RemoveField( - model_name='shoppinglistgroup', - name='is_deleted', - ), - migrations.RemoveField( - model_name='unitofmeasure', - name='deleted_at', - ), - migrations.RemoveField( - model_name='unitofmeasure', - name='is_active', - ), - migrations.RemoveField( - model_name='unitofmeasure', - name='is_deleted', - ), - migrations.RemoveField( - model_name='user', - name='is_active', - ), - ] diff --git a/backend/api/models/category.py b/backend/api/models/category.py index 446997f..58c73ac 100644 --- a/backend/api/models/category.py +++ b/backend/api/models/category.py @@ -2,7 +2,7 @@ from django.conf import settings from django.db import models from simple_history.models import HistoricalRecords - +from .shopping_list_group import ShoppingListGroup class Category(models.Model): class Meta: @@ -17,7 +17,7 @@ def __str__(self): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(name="name", unique=False, blank=False, null=False) - + shopping_list_group = models.ForeignKey(ShoppingListGroup, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True, null=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, diff --git a/backend/api/models/list_customization.py b/backend/api/models/list_customization.py index bb8fca1..9a94d46 100644 --- a/backend/api/models/list_customization.py +++ b/backend/api/models/list_customization.py @@ -10,9 +10,7 @@ class ListCustomization(models.Model): class Meta: verbose_name = 'List Customization' - constraints = [ - models.UniqueConstraint(fields=['shopping_list', 'item'], name="item--shopping-list") - ] + constraints = [models.UniqueConstraint(fields=['shopping_list', 'item'], name="item--shopping-list")] def __str__(self): return str(self.item) @@ -20,10 +18,10 @@ def __str__(self): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) shopping_list = models.ForeignKey(ShoppingList, on_delete=models.CASCADE) item = models.ForeignKey(Item, on_delete=models.CASCADE) - stocked = models.BooleanField(verbose_name='Stocked Here') - category = models.ForeignKey(Category, on_delete=models.CASCADE) - default_quantity = models.IntegerField(name="Default Quantity", default=1) - default_unit_of_measure = models.ForeignKey(UnitOfMeasure, on_delete=models.CASCADE) + stocked = models.BooleanField(verbose_name='Stocked Here', default=True) + category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, null=True) + default_quantity = models.IntegerField(name="Default Quantity", default=1, blank=True, null=True) + default_unit_of_measure = models.ForeignKey(UnitOfMeasure, on_delete=models.CASCADE, null=True, blank=True) purchase_count = models.IntegerField(name="Purchase Count", default=0) note = models.TextField(name="notes", null=True, blank=True) diff --git a/backend/api/models/list_item.py b/backend/api/models/list_item.py index 44f5eef..a849c02 100644 --- a/backend/api/models/list_item.py +++ b/backend/api/models/list_item.py @@ -1,6 +1,6 @@ import uuid -from api.models import UnitOfMeasure, Item, ShoppingList +from api.models import UnitOfMeasure, Item, ShoppingListGroup from django.conf import settings from django.db import models from simple_history.models import HistoricalRecords @@ -11,19 +11,18 @@ class ListItem(models.Model): class Meta: verbose_name = 'List Item' constraints = [ - models.UniqueConstraint(fields=['shopping_list', 'item'], name="item-shopping-list") + models.UniqueConstraint(fields=['shopping_list_group', 'item'], name="item-shopping-list") ] def __str__(self): return str(self.item) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - shopping_list = models.ForeignKey(ShoppingList, on_delete=models.CASCADE) + shopping_list_group = models.ForeignKey(ShoppingListGroup, on_delete=models.CASCADE) item = models.ForeignKey(Item, on_delete=models.CASCADE) - quantity = models.IntegerField(name="Purchase Count", default=0) + quantity = models.IntegerField(default=0) active = models.BooleanField(verbose_name='Active') completed = models.BooleanField(verbose_name='Completed') - unit_of_measure = models.ForeignKey(UnitOfMeasure, on_delete=models.CASCADE) # Metadata created_at = models.DateTimeField(auto_now_add=True, null=True) diff --git a/backend/api/models/shopping_list_group.py b/backend/api/models/shopping_list_group.py index 157670a..d5b14ae 100644 --- a/backend/api/models/shopping_list_group.py +++ b/backend/api/models/shopping_list_group.py @@ -2,7 +2,7 @@ from django.conf import settings from django.db import models from simple_history.models import HistoricalRecords -from api.models import Category + class ShoppingListGroup(models.Model): @@ -18,7 +18,6 @@ def __str__(self): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(name="name", unique=True, blank=False, null=False) - categories = models.ManyToManyField(Category) # Metadata created_at = models.DateTimeField(auto_now_add=True, null=True) diff --git a/backend/api/schemas/__init__.py b/backend/api/schemas/__init__.py index dc5ca96..a653167 100644 --- a/backend/api/schemas/__init__.py +++ b/backend/api/schemas/__init__.py @@ -1,8 +1,20 @@ +from ninja import Schema from .unit_of_measure import UnitOfMeasureIn, UnitOfMeasureOut -from .category import CategoryIn, CategoryOut +from .category import CategoryIn, CategoryOut, MinimizedCategorypOut from .user import UserIn, UserOut -from .shopping_list_group import ShoppingListGroupIn, ShoppingListGroupOut -from .shopping_list import ShoppingListIn, ShoppingListOut +from .shopping_list_group import ( + ShoppingListGroupIn, + ShoppingListGroupOut, + ShoppingListGroupAddItemOut, + MinimizedShoppingListGroupOut, + ShoppingListGroupAddItemIn, +) +from .shopping_list import ShoppingListIn, ShoppingListOut, ShoppingListOutMinimized from .item import ItemCreate, ItemUpdate, ItemOut from .list_customization import ListCustomizationIn, ListCustomizationOut -from .list_item import ListItemIn, ListItemOut \ No newline at end of file +from .list_item import ListItemIn, ListItemOut, ListItemUpdate +from .list_functions import ListFunctionsAddIn, ListFunctionsAddOut + + +class Error(Schema): + message: str diff --git a/backend/api/schemas/category.py b/backend/api/schemas/category.py index aaede7a..55bd36c 100644 --- a/backend/api/schemas/category.py +++ b/backend/api/schemas/category.py @@ -17,6 +17,11 @@ class Meta: class CategoryOut(ModelSchema): class Meta: - model=Category - fields='__all__' + model = Category + fields = '__all__' + +class MinimizedCategorypOut(ModelSchema): + class Meta: + model = Category + fields = ['id', 'name'] diff --git a/backend/api/schemas/item.py b/backend/api/schemas/item.py index c0eaae2..f9d5862 100644 --- a/backend/api/schemas/item.py +++ b/backend/api/schemas/item.py @@ -12,25 +12,14 @@ class Meta: model = Item exclude = ['created_at', 'updated_at', 'photo', 'created_by', 'updated_by'] + class ItemUpdate(ModelSchema): class Meta: model = Item exclude = ['created_at', 'updated_at', 'photo', 'created_by', 'updated_by'] + class ItemOut(ModelSchema): class Meta: model = Item - fields='__all__' - - # id: UUID - # name: str - # plural_name: str - # list_group: UUID - # default_quantity: int - # unit_of_measure: UUID - # category: UUID - # note: str - # created_at: datetime - # created_by_id: Optional[UUID] - # updated_at: datetime - # updated_by_id: Optional[UUID] + fields = '__all__' diff --git a/backend/api/schemas/list_functions.py b/backend/api/schemas/list_functions.py new file mode 100644 index 0000000..f997cdb --- /dev/null +++ b/backend/api/schemas/list_functions.py @@ -0,0 +1,50 @@ +from uuid import UUID + +from ninja import Schema +from typing import List +from api.schemas import ( + ShoppingListOutMinimized, + MinimizedCategorypOut, + MinimizedShoppingListGroupOut, +) + + +class ListFunctionsAddIn(Schema): + item_id: UUID + quantity: int + + +# Start combined shopping list out +class ItemOutMinimized(Schema): + + id: UUID + name: str + plural_name: str + unit_of_measure: str + + +class ListItemOutwithDetails(Schema): + id: UUID + active: bool + completed: bool + quantity: int + item: ItemOutMinimized + + +class CategoryDetails(Schema): + category: MinimizedCategorypOut + items: List[ListItemOutwithDetails] + + +class ListDetails(Schema): + shopping_list: ShoppingListOutMinimized + categories: List[CategoryDetails] + + +class ShoppingListGroupDetails(Schema): + shopping_list_group: MinimizedShoppingListGroupOut + lists: List[ListDetails] + + +class ListFunctionsAddOut(Schema): + lists: List[ShoppingListGroupDetails] diff --git a/backend/api/schemas/list_item.py b/backend/api/schemas/list_item.py index 34fd1b2..fe45f23 100644 --- a/backend/api/schemas/list_item.py +++ b/backend/api/schemas/list_item.py @@ -1,20 +1,22 @@ # schemas.py -from datetime import datetime from typing import Optional -from uuid import UUID - -from api.models import ListCustomization +from api.models import ListItem from ninja import ModelSchema, Schema class ListItemIn(ModelSchema): class Meta: - model = ListCustomization - exclude = ['created_at', 'updated_at', 'created_by', 'updated_by'] + model = ListItem + exclude = ['created_at', 'updated_at', 'created_by', 'updated_by', 'active', 'completed', 'id'] + + +class ListItemUpdate(Schema): + quantity: Optional[int] = None + active: Optional[bool] = None + completed: Optional[bool] = None class ListItemOut(ModelSchema): class Meta: - model = ListCustomization + model = ListItem fields = '__all__' - diff --git a/backend/api/schemas/shopping_list.py b/backend/api/schemas/shopping_list.py index 37bf707..1f7e529 100644 --- a/backend/api/schemas/shopping_list.py +++ b/backend/api/schemas/shopping_list.py @@ -18,5 +18,10 @@ class Meta: class ShoppingListOut(ModelSchema): class Meta: model = ShoppingList - fields='__all__' + fields = '__all__' + +class ShoppingListOutMinimized(ModelSchema): + class Meta: + model = ShoppingList + fields = ['id', 'name'] diff --git a/backend/api/schemas/shopping_list_group.py b/backend/api/schemas/shopping_list_group.py index fed98d1..f7d8e90 100644 --- a/backend/api/schemas/shopping_list_group.py +++ b/backend/api/schemas/shopping_list_group.py @@ -18,5 +18,20 @@ class Meta: class ShoppingListGroupOut(ModelSchema): class Meta: model = ShoppingListGroup - fields='__all__' - #categories: list[UUID] \ No newline at end of file + fields = '__all__' + + +class MinimizedShoppingListGroupOut(ModelSchema): + class Meta: + model = ShoppingListGroup + fields = ['id', 'name'] + + +class ShoppingListGroupAddItemOut(ModelSchema): + class Meta: + model = ShoppingListGroup + fields = '__all__' + + +class ShoppingListGroupAddItemIn(Schema): + item_id: UUID diff --git a/backend/api/views/__init__.py b/backend/api/views/__init__.py index 157572e..5137ca0 100644 --- a/backend/api/views/__init__.py +++ b/backend/api/views/__init__.py @@ -6,4 +6,5 @@ from .user import no_auth_router as user_no_auth_router from .item import router as item_router from .list_customization import router as list_customization_router -from .list_item import router as list_item_router \ No newline at end of file +from .list_item import router as list_item_router +from .list_functions import router as list_functions_router \ No newline at end of file diff --git a/backend/api/views/item.py b/backend/api/views/item.py index 5128c3f..8cfc45d 100644 --- a/backend/api/views/item.py +++ b/backend/api/views/item.py @@ -36,18 +36,18 @@ def create_item(request, payload: Form[ItemCreate], photo: File[UploadedFile] = item.photo.save(F'item-{item.id}.{item.name.split(".")[-1]}', photo) return item -@router.put('/{id}', response=ItemOut) -def update_item(request, payload: Form[ItemUpdate], photo: File[UploadedFile] = None): - user = get_user_model().objects.get(id=request.user.id) - item = get_object_or_404(Item, id=id, user=user) - for attr, value in payload.dict(exclude_unset=True).items(): - setattr(item, attr, value) - item.updated_by = user - item.save() - - if photo: - item.photo.save(F'item-{item.id}.{item.name.split(".")[-1]}', photo) +# @router.put('/{id}', response=ItemOut) +# def update_item(request, payload: Form[ItemUpdate], photo: File[UploadedFile] = None): +# user = get_user_model().objects.get(id=request.user.id) +# item = get_object_or_404(Item, id=id, user=user) +# for attr, value in payload.dict(exclude_unset=True).items(): +# setattr(item, attr, value) +# item.updated_by = user +# item.save() + +# if photo: +# item.photo.save(F'item-{item.id}.{item.name.split(".")[-1]}', photo) - return item +# return item diff --git a/backend/api/views/list_functions.py b/backend/api/views/list_functions.py new file mode 100644 index 0000000..853b5bb --- /dev/null +++ b/backend/api/views/list_functions.py @@ -0,0 +1,127 @@ +from collections import defaultdict +import logging +from uuid import UUID +from django.shortcuts import get_object_or_404 +from ninja import Form, Router + +from api.schemas import ListFunctionsAddIn, ListFunctionsAddOut, ListItemUpdate, ListItemOut +from api.models import Item, ListItem, ShoppingList, ShoppingListGroup, ListCustomization +from django.contrib.auth import get_user_model + +logger = logging.getLogger(__name__) + +router = Router() + + +def get_shopping_list_group_output(slg: UUID): + """ + Get shopping list group output taking into account list customization entries for items + + Args: + slg (UUID): shopping list group object id + + Returns: + _type_: customized list items + """ + slg_obj = ShoppingListGroup.objects.get(id=slg) + + # get all list_items from slg + slg_items = defaultdict(list) + for item in ListItem.objects.filter(shopping_list_group=slg_obj): + item_object = Item.objects.get(id=item.item.id) + category = item_object.category + combined_data = { + 'item': { + 'id': item_object.id, + 'name': item_object.name, + 'plural_name': item_object.plural_name, + 'unit_of_measure': F'{item_object.unit_of_measure}', + }, + 'id': item.id, + 'active': item.active, + 'completed': item.completed, + 'quantity': item.quantity, + } + slg_items[category].append(combined_data) + + # setup output + output = {'shopping_list_group': slg_obj, 'lists': []} + + for s_list in ShoppingList.objects.filter(list_group=slg_obj): + current_list = {'shopping_list': s_list, 'categories': []} + for category in slg_items.keys(): + customized_items = defaultdict(list) + + # check for customizations + for combined_item in slg_items[category]: + try: + # logger.error(combined_item) + custom_item = ListCustomization.objects.get(item=combined_item['item']['id'], shopping_list=s_list) + try: + uom = f'{custom_item.default_unit_of_measure}' + except AttributeError: + uom = combined_item['item']['unit_of_measure'] + customized_items[custom_item.category].append( + { + 'item': { + 'id': combined_item['item']['id'], + 'name': combined_item['item']['name'], + 'plural_name': combined_item['item']['plural_name'], + 'unit_of_measure': uom, + }, + 'id': combined_item['id'], + 'active': combined_item['active'], + 'completed': combined_item['completed'], + 'quantity': combined_item['quantity'], + } + ) + + except ListCustomization.DoesNotExist: + customized_items[category].append(combined_item) + for category, customized_items_list in customized_items.items(): + current_list['categories'].append({'category': category, 'items': customized_items_list}) + + output['lists'].append(current_list) + + return {'lists': [output]} + + +@router.get('/shopping_list_group/{id}', response=ListFunctionsAddOut) +def get_shopping_list_group_list(request, id: UUID): + return get_shopping_list_group_output(id) + + +@router.post('/list_item', response=ListFunctionsAddOut) +def add_item(request, payload: Form[ListFunctionsAddIn]): + + # add item to list + item = get_object_or_404(Item, id=payload.item_id) + list_item, created = ListItem.objects.get_or_create( + item=item, shopping_list_group=item.list_group, active=True, completed=False, defaults={'quantity': payload.quantity} + ) + + # increment quanity if item already exists + if not created: + list_item.quantity += payload.quantity + list_item.save() + + return get_shopping_list_group_output(item.list_group.id) + + +@router.put('/list_item/{id}', response=ListItemOut) +def update_item(request, id: UUID, payload: ListItemUpdate): + user = get_user_model().objects.get(id=request.user.id) + item = get_object_or_404(ListItem, id=id) + for attr, value in payload.dict(exclude_unset=True).items(): + setattr(item, attr, value) + item.updated_by = user + item.save() + + return item + + +@router.delete('/list_item/{id}') +def delete_item(request, id: UUID): + item = get_object_or_404(ListItem, id=id) + item.delete() + return {"success": True} diff --git a/backend/api/views/list_item.py b/backend/api/views/list_item.py index caf7dc2..45857e8 100644 --- a/backend/api/views/list_item.py +++ b/backend/api/views/list_item.py @@ -2,7 +2,7 @@ from typing import List from api.models import ListItem -from api.schemas import ListItemIn, ListItemOut +from api.schemas import ListItemIn, ListItemOut, ListItemUpdate from api.utils.model_utils import set_created_updated_by_on_create, set_updated_by_on_update from ninja import Router from ninja_crud import viewsets @@ -26,7 +26,7 @@ class ListItemViewSet(viewsets.APIViewSet): update = UpdateView( name='list_item_update', path='/{id}', - request_body=ListItemIn, + request_body=ListItemUpdate, response_body=ListItemOut, pre_save=set_updated_by_on_update, ) diff --git a/backend/api/views/shopping_list_group.py b/backend/api/views/shopping_list_group.py index 57af58b..62899ec 100644 --- a/backend/api/views/shopping_list_group.py +++ b/backend/api/views/shopping_list_group.py @@ -1,7 +1,7 @@ import logging from typing import List -from api.schemas import ShoppingListGroupIn, ShoppingListGroupOut +from api.schemas import ShoppingListGroupIn, ShoppingListGroupOut, ShoppingListGroupAddItemOut from api.utils.model_utils import ( set_created_updated_by_on_create, set_updated_by_on_update, @@ -46,3 +46,14 @@ class ShoppingListGroupViewSet(viewsets.APIViewSet): router = Router() ShoppingListGroupViewSet.add_views_to(router) + +# @router.post('/{id}/add_item', response=ShoppingListGroupAddItemOut) +# def cslg_add_item(request, payload: Form[ItemCreate] = None): +# user = get_user_model().objects.get(id=request.user.id) +# options = payload.dict(exclude_unset=True) +# options["created_by"] = get_user_model().objects.get(id=request.user.id) +# item = Item.objects.create(**options) +# item.created_by = user +# if photo: +# item.photo.save(F'item-{item.id}.{item.name.split(".")[-1]}', photo) +# return item \ No newline at end of file diff --git a/backend/scripts/load_data.py b/backend/scripts/load_data.py index 840869e..5d806dd 100644 --- a/backend/scripts/load_data.py +++ b/backend/scripts/load_data.py @@ -1,8 +1,8 @@ -from api.models import UnitOfMeasure, Category, ShoppingListGroup, ShoppingList, Item +import random +from api.models import UnitOfMeasure, Category, ShoppingListGroup, ShoppingList, Item, ListItem, ListCustomization from django.contrib.auth import get_user_model import logging - logger = logging.getLogger("api.scripts.load_uom") @@ -45,53 +45,54 @@ def run(): {"name": "Head", "plural_name": "Heads"}, ] - category_data = [ - 'Bakery', - 'Deli', - 'Dairy', - 'Baking Supplies', - 'Canned Meats', - 'Snacks-Chips', - 'Snacks-Pretzels', - 'Snacks-Cookies', - 'Snacks-Crackers', - 'Snacks-Nuts', - 'Canned Vegetables', - 'Canned Soup', - 'Paper Products', - 'Juice', - 'Soda', - 'Pharmacy', - 'Beverages', - 'Frozen Treats/Ice Cream', - 'Frozen Dinners', - 'Frozen Vegetables', - 'Frozen Other', - 'Laundry Supplies', - 'Meat', - 'Seafood', - 'Produce', - 'Condiments', - 'Mexican', - 'Asian', - 'Italian/Pasta/Sauce', - 'Cereal', - 'Bread', - 'Alcohol', - 'Floral', - 'Pet Food', - 'Cleaning Products', - 'Hair Products - Shampoo/Conditioner', - 'Automotive', - 'Office Supplies', - 'Beauty Supplies', - 'Other Breakfast', - 'Coffee/Tea', - ] - shopping_list_group_data = [ - 'Grocery', - 'Home Improvement', + { + 'name': 'Grocery', + 'categories': [ + 'Bakery', + 'Deli', + 'Dairy', + 'Baking Supplies', + 'Canned Meats', + 'Snacks-Chips', + 'Snacks-Pretzels', + 'Snacks-Cookies', + 'Snacks-Crackers', + 'Snacks-Nuts', + 'Canned Vegetables', + 'Canned Soup', + 'Paper Products', + 'Juice', + 'Soda', + 'Pharmacy', + 'Beverages', + 'Frozen Treats/Ice Cream', + 'Frozen Dinners', + 'Frozen Vegetables', + 'Frozen Other', + 'Laundry Supplies', + 'Meat', + 'Seafood', + 'Produce', + 'Condiments', + 'Mexican', + 'Asian', + 'Italian/Pasta/Sauce', + 'Cereal', + 'Bread', + 'Alcohol', + 'Floral', + 'Pet Food', + 'Cleaning Products', + 'Hair Products - Shampoo/Conditioner', + 'Automotive', + 'Office Supplies', + 'Beauty Supplies', + 'Other Breakfast', + 'Coffee/Tea', + ], + }, + {'name': 'Home Improvement', 'categories': ['Tools', 'Hardware', 'Plumbing']}, ] shopping_list_data = [ @@ -218,12 +219,94 @@ def run(): {'name': 'Salmon', 'plural_name': 'Salmon', 'default_quantity': 1, 'unit_of_measure': 'Pound'}, {'name': 'Fresh Shrimp', 'plural_name': 'Fresh Shrimp', 'default_quantity': 1, 'unit_of_measure': 'Pound'}, ], + 'Asian': [ + {'name': 'Soy Sauce', 'plural_name': 'Soy Sauce', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Chili Crisp', 'plural_name': 'Chili Crisp', 'default_quantity': 1, 'unit_of_measure': 'Jar'} + ], 'Mexican': [ {'name': 'Taco Shell', 'plural_name': 'Taco Shells', 'default_quantity': 1, 'unit_of_measure': 'Package'}, {'name': 'Salsa', 'plural_name': 'Salsa', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, {'name': 'Taco Seasoning', 'plural_name': 'Taco Seasoning', 'default_quantity': 1, 'unit_of_measure': 'Package'}, {'name': 'Tortillas', 'plural_name': 'Tortillas', 'default_quantity': 1, 'unit_of_measure': 'Package'}, - ] + ], + 'Frozen Vegetables': [ + {'name': 'Frozen Spinach', 'plural_name': 'Frozen Spinach', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Frozen Broccoli', 'plural_name': 'Frozen Broccoli', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Frozen Corn', 'plural_name': 'Frozen Corn', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Frozen Green Beans', 'plural_name': 'Frozen Green Beans', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Frozen Peas', 'plural_name': 'Frozen Peas', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Frozen Strawberries', 'plural_name': 'Frozen Strawberries', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Frozen Blueberries', 'plural_name': 'Frozen Blueberries', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Frozen Tater Tots', 'plural_name': 'Frozen Tater Tots', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Frozen French Fries', 'plural_name': 'Frozen French Fries', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + ], + 'Baking Supplies': [ + {'name': 'Vegetable Oil', 'plural_name': 'Vegetable Oil', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Olive Oil', 'plural_name': 'Olive Oil', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Semi-Sweet Chocolate Chips', 'plural_name': 'Semi-Sweet Chocolate Chips', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Dark Chocolate Chips', 'plural_name': 'Dark Chocolate Chips', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Marshmallows', 'plural_name': 'Marshmallows', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Cornbread Mix', 'plural_name': 'Cornbread Mix', 'default_quantity': 1, 'unit_of_measure': 'Box'}, + {'name': 'Flour', 'plural_name': 'Flour', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Chia Seeds', 'plural_name': 'Chia Seeds', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Flaxseed', 'plural_name': 'Flaxseed', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Baking Soda', 'plural_name': 'Baking Soda', 'default_quantity': 1, 'unit_of_measure': 'Box'}, + {'name': 'Baking Powder', 'plural_name': 'Baking Powder', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Wheat Flour', 'plural_name': 'Wheat Flour', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Sugar', 'plural_name': 'Sugar', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Confectioners Sugar', 'plural_name': 'Confectioners Sugar', 'default_quantity': 1, 'unit_of_measure': 'Bag'}, + {'name': 'Cinnamon', 'plural_name': 'Cinnamon', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Salt', 'plural_name': 'Salt', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Ground Pepper', 'plural_name': 'Ground Pepper', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Chili Powder', 'plural_name': 'Chili Powder', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Basil (dried)', 'plural_name': 'Basil (dried)', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Oregano', 'plural_name': 'Oregano', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Nutmeg', 'plural_name': 'Nutmeg', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Ground Ginger', 'plural_name': 'Ground Ginger', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + ], + 'Italian/Pasta/Sauce': [ + {'name': 'Pasta Sauce', 'plural_name': 'Pasta Sauce', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Pizza Sauce', 'plural_name': 'Pizza Sauce', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Spaghetti', 'plural_name': 'Spaghetti', 'default_quantity': 1, 'unit_of_measure': 'Box'}, + {'name': 'Pasta - Shells', 'plural_name': 'Pasta - Shells', 'default_quantity': 1, 'unit_of_measure': 'Box'}, + {'name': 'Pasta - Bowtie', 'plural_name': 'Pasta - Bowtie', 'default_quantity': 1, 'unit_of_measure': 'Box'}, + {'name': 'Pasta - Rotini', 'plural_name': 'Pasta - Rotini', 'default_quantity': 1, 'unit_of_measure': 'Box'}, + {'name': 'Pasta - Tricolor-Rotini', 'plural_name': 'Pasta - Tricolor-Rotini', 'default_quantity': 1, 'unit_of_measure': 'Box'}, + {'name': 'Linguini', 'plural_name': 'Linguini', 'default_quantity': 1, 'unit_of_measure': 'Box'}, + {'name': 'Fettuccine', 'plural_name': 'Fettuccine', 'default_quantity': 1, 'unit_of_measure': 'Box'}, + ], + 'Juice': [ + {'name': 'Tomato Juice', 'plural_name': 'Tomato Juice', 'default_quantity': 1, 'unit_of_measure': 'Bottle'}, + {'name': 'Cranberry Juice', 'plural_name': 'Cranberry Juice', 'default_quantity': 1, 'unit_of_measure': 'Bottle'}, + ], + 'Soda': [ + {'name': 'Coke', 'plural_name': 'Coke', 'default_quantity': 1, 'unit_of_measure': 'Case'}, + {'name': 'Diet Coke', 'plural_name': 'Diet Coke', 'default_quantity': 1, 'unit_of_measure': 'Case'}, + {'name': 'Bottled Water', 'plural_name': 'Bottled Water', 'default_quantity': 1, 'unit_of_measure': 'Bottle'}, + {'name': 'Seltzer Water', 'plural_name': 'Seltzer Water', 'default_quantity': 1, 'unit_of_measure': 'Bottle'}, + {'name': 'Sparkling Water', 'plural_name': 'Sparkling Water', 'default_quantity': 1, 'unit_of_measure': 'Bottle'}, + ], + 'Frozen Treats/Ice Cream': [ + {'name': 'Ice Cream', 'plural_name': 'Ice Cream', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + ], + 'Canned Meats': [ + {'name': 'Canned Tuna in Water', 'plural_name': 'Canned Tuna in Water', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Canned Tuna in Oil', 'plural_name': 'Canned Tuna in Oil', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Canned Salmon', 'plural_name': 'Canned Salmon', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + {'name': 'Salmon Pouch', 'plural_name': 'Salmon Pouch', 'default_quantity': 1, 'unit_of_measure': 'Container'}, + ], + 'Condiments': [ + {'name': 'Blue Cheese Salad Dressing', 'plural_name': 'Blue Cheese Salad Dressing', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Ranch Salad Dressing', 'plural_name': 'Ranch Salad Dressing', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Balsamic Vinaigrette Salad Dressing', 'plural_name': 'Balsamic Vinaigrette Salad Dressing', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Worcestershire Sauce', 'plural_name': 'Worcestershire Sauce', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Hot sauce', 'plural_name': 'Hot Sauce', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Barbecue Sauce', 'plural_name': 'Barbecue Sauce', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Dijon Mustard', 'plural_name': 'Dijon Mustard', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Brown Mustard', 'plural_name': 'Brown Mustard', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Ketchup', 'plural_name': 'Ketchup', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + {'name': 'Mayonnaise', 'plural_name': 'Mayonnaise', 'default_quantity': 1, 'unit_of_measure': 'Jar'}, + ], }, 'Home Improvement': {}, } @@ -260,70 +343,8 @@ def run(): # {'name': 'Cream of Mushroom Soup', 'plural_name': 'Cream of Mushroom Soup', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, # {'name': 'Cream of Chicken Soup', 'plural_name': 'Cream of Chicken Soup', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, # {'name': 'Vegetable Soup', 'plural_name': 'Vegetable Soup', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Blue Cheese Salad Dressing', 'plural_name': 'Blue Cheese Salad Dressing', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Ranch Salad Dressing', 'plural_name': 'Ranch Salad Dressing', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Balsamic Vinaigrette Salad Dressing', 'plural_name': 'Balsamic Vinaigrette Salad Dressing', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Worcestershire Sauce', 'plural_name': 'Worcestershire Sauce', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Soy Sauce', 'plural_name': 'Soy Sauce', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Apple Juice', 'plural_name': 'Apple Juice', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'White Grape Juice', 'plural_name': 'White Grape Juice', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Grape Juice', 'plural_name': 'Grape Juice', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Tomato Juice', 'plural_name': 'Tomato Juice', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Cranberry Juice', 'plural_name': 'Cranberry Juice', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Coke', 'plural_name': 'Coke', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Diet Coke', 'plural_name': 'Diet Coke', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Bottled Water', 'plural_name': 'Bottled Water', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Seltzer Water', 'plural_name': 'Seltzer Water', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Sparkling Water', 'plural_name': 'Sparkling Water', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Ice Cream', 'plural_name': 'Ice Cream', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Spinach', 'plural_name': 'Frozen Spinach', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Broccoli', 'plural_name': 'Frozen Broccoli', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Corn', 'plural_name': 'Frozen Corn', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Green Beans', 'plural_name': 'Frozen Green Beans', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Peas', 'plural_name': 'Frozen Peas', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Strawberries', 'plural_name': 'Frozen Strawberries', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Blueberries', 'plural_name': 'Frozen Blueberries', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Waffles', 'plural_name': 'Frozen Waffles', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Pancakes', 'plural_name': 'Frozen Pancakes', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen Tater Tots', 'plural_name': 'Frozen Tater Tots', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Frozen French Fries', 'plural_name': 'Frozen French Fries', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, # {'name': 'Pierogies', 'plural_name': 'Pierogies', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Ketchup', 'plural_name': 'Ketchup', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'BBQ Sauce', 'plural_name': 'BBQ Sauce', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, # {'name': 'Black Olives', 'plural_name': 'Black Olives', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Vegetable Oil', 'plural_name': 'Vegetable Oil', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Olive Oil', 'plural_name': 'Olive Oil', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Semi-Sweet Chocolate Chips', 'plural_name': 'Semi-Sweet Chocolate Chips', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Dark Chocolate Chips', 'plural_name': 'Dark Chocolate Chips', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Marshmallows', 'plural_name': 'Marshmallows', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Cornbread Mix', 'plural_name': 'Cornbread Mix', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Flour', 'plural_name': 'Flour', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Chia Seeds', 'plural_name': 'Chia Seeds', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Flaxseed', 'plural_name': 'Flaxseed', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Baking Soda', 'plural_name': 'Baking Soda', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Baking Powder', 'plural_name': 'Baking Powder', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Wheat Flour', 'plural_name': 'Wheat Flour', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Sugar', 'plural_name': 'Sugar', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Confectioners Sugar', 'plural_name': 'Confectioners Sugar', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Cinnamon', 'plural_name': 'Cinnamon', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Salt', 'plural_name': 'Salt', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Ground Pepper', 'plural_name': 'Ground Pepper', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Chili Powder', 'plural_name': 'Chili Powder', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Basil (dried)', 'plural_name': 'Basil (dried)', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Oregano', 'plural_name': 'Oregano', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Nutmeg', 'plural_name': 'Nutmeg', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Ground Ginger', 'plural_name': 'Ground Ginger', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Spaghetti Sauce', 'plural_name': 'Spaghetti Sauce', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Pizza Sauce', 'plural_name': 'Pizza Sauce', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Spaghetti', 'plural_name': 'Spaghetti', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Pasta - Shells', 'plural_name': 'Pasta - Shells', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Pasta - Bowtie', 'plural_name': 'Pasta - Bowtie', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Pasta - Rotini', 'plural_name': 'Pasta - Rotini', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Pasta - Tricolor-Rotini', 'plural_name': 'Pasta - Tricolor-Rotini', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Canned Tuna in Water', 'plural_name': 'Canned Tuna in Water', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Canned Tuna in Oil', 'plural_name': 'Canned Tuna in Oil', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Canned Salmon', 'plural_name': 'Canned Salmon', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Salmon Pouch', 'plural_name': 'Salmon Pouch', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, # {'name': 'Dishwashing Liquid', 'plural_name': 'Dishwashing Liquid', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, # {'name': 'Dishwashing Detergent', 'plural_name': 'Dishwashing Detergent', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, # {'name': 'Sponges', 'plural_name': 'Sponges', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, @@ -339,29 +360,50 @@ def run(): # {'name': 'Steel Wool Pads', 'plural_name': 'Steel Wool Pads', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, # {'name': 'Paper Towels', 'plural_name': 'Paper Towels', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, # {'name': 'Toilet Paper', 'plural_name': 'Toilet Paper', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, - # {'name': 'Maple Syrup', 'plural_name': 'Maple Syrup', 'default_quantity': 1, 'unit_of_measure': 'Piece'}, + + # list customization data + list_customization = [ + { + 'shopping_list_group': 'Grocery', + 'list': 'Costco', + 'category': 'Produce', + 'name': 'Banana', + 'customization': {'default_unit_of_measure': 'Case'}, + }, + { + 'shopping_list_group': 'Grocery', + 'list': 'Kroger', + 'category': 'Produce', + 'name': 'Apple', + 'customization': {'stocked': False}, + }, + ] + + # create unit of measure for obj in uom_data: o, created = UnitOfMeasure.objects.get_or_create(name=obj['name'], plural_name=obj['plural_name'], created_by=user) if created: logger.info(F"Added: {obj['name']}") - for obj in category_data: - o, created = Category.objects.get_or_create(name=obj, created_by=user) - if created: - logger.info(F"Added: {obj}") - + # create shopping list groups for obj in shopping_list_group_data: - o, created = ShoppingListGroup.objects.get_or_create(name=obj, created_by=user) + o, created = ShoppingListGroup.objects.get_or_create(name=obj['name'], created_by=user) if created: - logger.info(F"Added: {obj}") + logger.info(F"Added: {obj['name']}") + for category in obj['categories']: + c, c_created = Category.objects.get_or_create(name=category, shopping_list_group=o, created_by=user) + if c_created: + logger.info(F"Added: {category}") + # create shopping lists for obj in shopping_list_data: slg = ShoppingListGroup.objects.get(name=obj[1]) o, created = ShoppingList.objects.get_or_create(name=obj[0], list_group=slg, created_by=user) if created: logger.info(F"Added: {obj[0]}") + # create items for list_group, categories in item_data.items(): slg = ShoppingListGroup.objects.get(name=list_group) for category, cat_items in categories.items(): @@ -378,3 +420,21 @@ def run(): ) if created: logger.info(F"Added: {item['name']}") + + # create list customazation objects + for custom in list_customization: + slg = ShoppingListGroup.objects.get(name=custom['shopping_list_group']) + s_list = ShoppingList.objects.get(name=custom['list'], list_group=slg) + category = Category.objects.get(name=custom['category'], shopping_list_group=slg) + item = Item.objects.get(name=custom['name'], list_group=slg, category=category) + + list_customization_obj, created = ListCustomization.objects.get_or_create( + shopping_list=s_list, category=category, item=item + ) + for key, value in custom['customization'].items(): + if key == 'default_unit_of_measure': + list_customization_obj.default_unit_of_measure = UnitOfMeasure.objects.get(name=value) + else: + setattr(list_customization_obj, key, value) + list_customization_obj.save() + logger.info(F"Customized item {custom['name']}")